888ca09d |
/*
* $Id$
*/
#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"
|
e60a9728 |
#include "config.h"
|
888ca09d |
#include "msg_parser.h"
#include "route.h"
#include "dprint.h"
|
e60a9728 |
#include "udp_server.h"
|
1b1b19d8 |
#include "globals.h"
|
831faabf |
#include "data_lump.h"
|
888ca09d |
|
03150098 |
#ifdef DEBUG_DMALLOC
#include <dmalloc.h>
#endif
|
888ca09d |
#define MAX_VIA_LINE_SIZE 240
#define MAX_RECEIVED_SIZE 57
|
0a974a1d |
#ifdef STATS
|
5b253cc6 |
#include "stats.h"
|
0a974a1d |
#endif
|
888ca09d |
|
b2e71d5b |
static char q_inet_itoa_buf[16]; /* 123.567.901.345\0 */
/* faster than inet_ntoa */
__inline char* q_inet_itoa(unsigned long ip)
{
unsigned char* p;
unsigned char a,b,c; /* abc.def.ghi.jkl */
int offset;
int r;
p=(unsigned char*)&ip;
offset=0;
/* unrolled loops (faster)*/
for(r=0;r<3;r++){
a=p[r]/100;
c=p[r]%10;
b=p[r]%100/10;
if (a){
q_inet_itoa_buf[offset]=a+'0';
q_inet_itoa_buf[offset+1]=b+'0';
q_inet_itoa_buf[offset+2]=c+'0';
q_inet_itoa_buf[offset+3]='.';
offset+=4;
}else if (b){
q_inet_itoa_buf[offset]=b+'0';
q_inet_itoa_buf[offset+1]=c+'0';
q_inet_itoa_buf[offset+2]='.';
offset+=3;
}else{
q_inet_itoa_buf[offset]=c+'0';
q_inet_itoa_buf[offset+1]='.';
offset+=2;
}
}
/* last number */
a=p[r]/100;
c=p[r]%10;
b=p[r]%100/10;
if (a){
q_inet_itoa_buf[offset]=a+'0';
q_inet_itoa_buf[offset+1]=b+'0';
q_inet_itoa_buf[offset+2]=c+'0';
q_inet_itoa_buf[offset+3]=0;
}else if (b){
q_inet_itoa_buf[offset]=b+'0';
q_inet_itoa_buf[offset+1]=c+'0';
q_inet_itoa_buf[offset+2]=0;
}else{
q_inet_itoa_buf[offset]=c+'0';
q_inet_itoa_buf[offset+1]=0;
}
return q_inet_itoa_buf;
}
|
1b1b19d8 |
/* checks if ip is in host(name) and ?host(ip)=name?
* ip must be in network byte order!
* resolver = DO_DNS | DO_REV_DNS; if 0 no dns check is made
* return 0 if equal */
int check_address(unsigned long ip, char *name, int resolver)
{
struct hostent* he;
int i;
/* maybe we are lucky and name it's an ip */
|
b2e71d5b |
if (strcmp(name, q_inet_itoa( /* *(struct in_addr *)&*/ip ))==0)
|
1b1b19d8 |
return 0;
if (resolver&DO_DNS){
|
b2e71d5b |
DBG("check_address: doing dns lookup\n");
|
1b1b19d8 |
/* try all names ips */
he=gethostbyname(name);
|
3e429f5c |
for(i=0;he && he->h_addr_list[i];i++){
|
1b1b19d8 |
if (*(unsigned long*)he->h_addr_list[i]==ip)
return 0;
}
}
if (resolver&DO_REV_DNS){
|
b2e71d5b |
DBG("check_address: doing rev. dns lookup\n");
print_ip(ip);
|
1b1b19d8 |
/* try reverse dns */
|
cf398313 |
he=gethostbyaddr((char*)&ip, sizeof(ip), AF_INET);
|
3e429f5c |
if (he && (strcmp(he->h_name, name)==0))
|
1b1b19d8 |
return 0;
|
3e429f5c |
for (i=0; he && he->h_aliases[i];i++){
|
1b1b19d8 |
if (strcmp(he->h_aliases[i],name)==0)
return 0;
}
}
return -1;
}
|
3e429f5c |
int forward_request( struct sip_msg* msg, struct proxy_l * p)
|
888ca09d |
{
|
7268726e |
unsigned int len, new_len, via_len, received_len, uri_len;
|
831faabf |
char* line_buf;
char* received_buf;
|
b2e71d5b |
char* tmp;
int tmp_len;
|
888ca09d |
char* new_buf;
|
4ac74c03 |
char* orig;
char* buf;
|
3e429f5c |
unsigned int offset, s_offset, size;
|
dc862225 |
struct sockaddr_in* to;
|
3e429f5c |
unsigned long source_ip;
|
831faabf |
struct lump *t,*r;
struct lump* anchor;
|
888ca09d |
|
4ac74c03 |
orig=msg->orig;
buf=msg->buf;
len=msg->len;
|
3e429f5c |
source_ip=msg->src_ip;
|
888ca09d |
received_len=0;
|
dc862225 |
new_buf=0;
|
831faabf |
line_buf=0;
received_buf=0;
|
dc862225 |
to=0;
to=(struct sockaddr_in*)malloc(sizeof(struct sockaddr));
if (to==0){
|
efeaaf53 |
LOG(L_ERR, "ERROR: forward_reply: out of memory\n");
|
dc862225 |
goto error;
}
|
888ca09d |
|
831faabf |
line_buf=malloc(sizeof(char)*MAX_VIA_LINE_SIZE);
if (line_buf==0){
LOG(L_ERR, "ERROR: forward_request: out of memory\n");
goto error1;
}
|
b2e71d5b |
/*
|
888ca09d |
via_len=snprintf(line_buf, MAX_VIA_LINE_SIZE, "Via: SIP/2.0/UDP %s:%d\r\n",
|
1b1b19d8 |
names[0], port_no);
|
b2e71d5b |
*/
via_len=MY_VIA_LEN+names_len[0]; /* space included in MY_VIA*/
if ((via_len+port_no_str_len+CRLF_LEN)<MAX_VIA_LINE_SIZE){
memcpy(line_buf, MY_VIA, MY_VIA_LEN);
memcpy(line_buf+MY_VIA_LEN, names[0], names_len[0]);
if (port_no!=SIP_PORT){
memcpy(line_buf+via_len, port_no_str, port_no_str_len);
via_len+=port_no_str_len;
}
memcpy(line_buf+via_len, CRLF, CRLF_LEN);
via_len+=CRLF_LEN;
line_buf[via_len]=0; /* null terminate the string*/
}else{
LOG(L_ERR, "forward_request: ERROR: via too long (%d)\n",
via_len);
goto error1;
}
|
888ca09d |
/* check if received needs to be added */
|
1b1b19d8 |
if (check_address(source_ip, msg->via1.host, received_dns)!=0){
|
831faabf |
received_buf=malloc(sizeof(char)*MAX_RECEIVED_SIZE);
if (received_buf==0){
LOG(L_ERR, "ERROR: forward_request: out of memory\n");
goto error1;
}
|
b2e71d5b |
/*
|
1b1b19d8 |
received_len=snprintf(received_buf, MAX_RECEIVED_SIZE,
";received=%s",
inet_ntoa(*(struct in_addr *)&source_ip));
|
b2e71d5b |
*/
memcpy(received_buf, RECEIVED, RECEIVED_LEN);
tmp=q_inet_itoa( /* *(struct in_addr *)& */source_ip);
tmp_len=strlen(tmp);
received_len=RECEIVED_LEN+tmp_len;
memcpy(received_buf+RECEIVED_LEN, tmp, tmp_len);
received_buf[received_len]=0; /*null terminate it */
|
1b1b19d8 |
}
|
888ca09d |
|
831faabf |
/* add via header to the list */
/* try to add it before msg. 1st via */
/*add first via, as an anchor for second via*/
anchor=anchor_lump(&(msg->add_rm), msg->via1.hdr-buf, 0, HDR_VIA);
if (anchor==0) goto error;
if (insert_new_lump_before(anchor, line_buf, via_len, HDR_VIA)==0)
goto error;
/* if received needs to be added, add anchor after host and add it */
if (received_len){
if (msg->via1.params){
size= msg->via1.params-msg->via1.hdr-1; /*compensate for ';' */
}else{
size= msg->via1.host-msg->via1.hdr+strlen(msg->via1.host);
if (msg->via1.port!=0){
size+=strlen(msg->via1.hdr+size+1)+1; /* +1 for ':'*/
}
}
anchor=anchor_lump(&(msg->add_rm), msg->via1.hdr-buf+size, 0, HDR_VIA);
if (anchor==0) goto error;
if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA)
==0 ) goto error;
}
|
34fd2612 |
/* compute new msg len and fix overlapping zones*/
|
831faabf |
new_len=len;
|
34fd2612 |
s_offset=0;
|
831faabf |
for(t=msg->add_rm;t;t=t->next){
for(r=t->before;r;r=r->before){
switch(r->op){
case LUMP_ADD:
new_len+=r->len;
break;
default:
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid op for"
" data lump (%x)\n", r->op);
}
}
switch(t->op){
case LUMP_ADD:
new_len+=t->len;
break;
case LUMP_DEL:
|
34fd2612 |
/* fix overlapping deleted zones */
if (t->u.offset < s_offset){
/* change len */
if (t->len>s_offset-t->u.offset)
t->len-=s_offset-t->u.offset;
else t->len=0;
t->u.offset=s_offset;
}
s_offset=t->u.offset+t->len;
|
831faabf |
new_len-=t->len;
break;
case LUMP_NOP:
|
34fd2612 |
/* fix offset if overlapping on a deleted zone */
if (t->u.offset < s_offset){
t->u.offset=s_offset;
}else
s_offset=t->u.offset;
|
831faabf |
/* do nothing */
break;
debug:
LOG(L_CRIT,"BUG:forward_request: invalid"
" op for data lump (%x)\n", r->op);
}
for (r=t->after;r;r=r->after){
switch(r->op){
case LUMP_ADD:
new_len+=r->len;
break;
default:
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid"
" op for data lump (%x)\n", r->op);
}
}
}
|
7268726e |
if (msg->new_uri){
uri_len=strlen(msg->new_uri);
new_len=new_len-strlen(msg->first_line.u.request.uri)+uri_len;
}
|
888ca09d |
new_buf=(char*)malloc(new_len+1);
if (new_buf==0){
|
efeaaf53 |
LOG(L_ERR, "ERROR: forward_request: out of memory\n");
|
dc862225 |
goto error;
|
888ca09d |
}
|
7268726e |
|
e60a9728 |
offset=s_offset=0;
|
7268726e |
if (msg->new_uri){
/* copy message up to uri */
size=msg->first_line.u.request.uri-buf;
memcpy(new_buf, orig, size);
offset+=size;
s_offset+=size;
/* add our uri */
memcpy(new_buf+offset, msg->new_uri, uri_len);
offset+=uri_len;
s_offset+=strlen(msg->first_line.u.request.uri); /* skip original uri */
}
|
831faabf |
/* copy msg adding/removing lumps */
for (t=msg->add_rm;t;t=t->next){
switch(t->op){
case LUMP_ADD:
/* just add it here! */
|
34fd2612 |
/* process before */
for(r=t->before;r;r=r->before){
switch (r->op){
case LUMP_ADD:
/*just add it here*/
memcpy(new_buf+offset, r->u.value, r->len);
offset+=r->len;
break;
default:
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid op for"
" data lump (%x)\n", r->op);
}
}
/* copy "main" part */
|
831faabf |
memcpy(new_buf+offset, t->u.value, t->len);
offset+=t->len;
|
34fd2612 |
/* process after */
for(r=t->after;r;r=r->after){
switch (r->op){
case LUMP_ADD:
/*just add it here*/
memcpy(new_buf+offset, r->u.value, r->len);
offset+=r->len;
break;
default:
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid op for"
" data lump (%x)\n", r->op);
}
}
|
831faabf |
break;
case LUMP_NOP:
case LUMP_DEL:
/* copy till offset */
if (s_offset>t->u.offset){
|
34fd2612 |
DBG("Warning: (%d) overlapped lumps offsets,"
" ignoring(%x, %x)\n", t->op, s_offset,t->u.offset);
/* this should've been fixed above (when computing len) */
/* just ignore it*/
break;
|
831faabf |
}
size=t->u.offset-s_offset;
if (size){
memcpy(new_buf+offset, orig+s_offset,size);
offset+=size;
s_offset+=size;
}
/* process before */
for(r=t->before;r;r=r->before){
switch (r->op){
case LUMP_ADD:
/*just add it here*/
memcpy(new_buf+offset, r->u.value, r->len);
offset+=r->len;
break;
|
34fd2612 |
default:
|
831faabf |
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid op for"
" data lump (%x)\n", r->op);
}
}
/* process main (del only) */
if (t->op==LUMP_DEL){
/* skip len bytes from orig msg */
s_offset+=t->len;
}
/* process after */
for(r=t->after;r;r=r->after){
switch (r->op){
case LUMP_ADD:
/*just add it here*/
memcpy(new_buf+offset, r->u.value, r->len);
offset+=r->len;
break;
default:
/* only ADD allowed for before/after */
LOG(L_CRIT, "BUG:forward_request: invalid op for"
" data lump (%x)\n", r->op);
}
|
888ca09d |
}
|
831faabf |
break;
default:
LOG(L_CRIT, "BUG: forward_request: unknown op (%x)\n",
t->op);
|
888ca09d |
}
}
|
831faabf |
/* copy the rest of the message */
memcpy(new_buf+offset, orig+s_offset, len-s_offset);
|
888ca09d |
new_buf[new_len]=0;
/* send it! */
|
efeaaf53 |
DBG("Sending:\n%s.\n", new_buf);
DBG("orig. len=%d, new_len=%d, via_len=%d, received_len=%d\n",
|
e60a9728 |
len, new_len, via_len, received_len);
|
dc862225 |
to->sin_family = AF_INET;
|
4ac74c03 |
to->sin_port = (p->port)?htons(p->port):htons(SIP_PORT);
|
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++;
p->ok=1;
|
e60a9728 |
}
/* ? not 64bit clean?*/
|
4ac74c03 |
to->sin_addr.s_addr=*((long*)p->host.h_addr_list[p->addr_idx]);
|
e60a9728 |
|
4ac74c03 |
p->tx++;
p->tx_bytes+=new_len;
|
0a974a1d |
#ifdef STATS
|
5b253cc6 |
stats.total_tx++;
|
0a974a1d |
#endif
|
5b253cc6 |
|
3e429f5c |
if (udp_send(new_buf, new_len, (struct sockaddr*) to,
sizeof(struct sockaddr_in))==-1){
|
4ac74c03 |
p->errors++;
p->ok=0;
|
e60a9728 |
goto error;
|
0a974a1d |
}
#ifdef STATS
else stats.ok_tx_rq++;
#endif
|
e60a9728 |
free(new_buf);
|
dc862225 |
free(to);
|
831faabf |
/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
|
888ca09d |
return 0;
|
831faabf |
error1:
if (line_buf) free(line_buf);
if (received_buf) free(received_buf);
|
888ca09d |
error:
|
dc862225 |
if (new_buf) free(new_buf);
if (to) free(to);
|
888ca09d |
return -1;
}
/* removes first via & sends msg to the second */
|
4ac74c03 |
int forward_reply(struct sip_msg* msg)
|
888ca09d |
{
|
1b1b19d8 |
unsigned int new_len, via_len,r;
|
888ca09d |
char* new_buf;
|
3e429f5c |
unsigned offset, s_offset, size;
|
e60a9728 |
struct hostent* he;
|
dc862225 |
struct sockaddr_in* to;
|
4ac74c03 |
char* orig;
char* buf;
unsigned int len;
|
888ca09d |
|
4ac74c03 |
orig=msg->orig;
buf=msg->buf;
len=msg->len;
|
1b1b19d8 |
new_buf=0;
|
dc862225 |
to=0;
to=(struct sockaddr_in*)malloc(sizeof(struct sockaddr));
if (to==0){
|
efeaaf53 |
LOG(L_ERR, "ERROR: forward_reply: out of memory\n");
|
dc862225 |
goto error;
}
|
888ca09d |
|
1b1b19d8 |
/*check if first via host = us */
if (check_via){
for (r=0; r<addresses_no; r++)
if(strcmp(msg->via1.host, names[r])==0) break;
if (r==addresses_no){
|
efeaaf53 |
LOG(L_NOTICE, "ERROR: forward_reply: host in first via!=me : %s\n",
|
1b1b19d8 |
msg->via1.host);
/* send error msg back? */
goto error;
}
}
|
e60a9728 |
/* we must remove the first via */
|
888ca09d |
via_len=msg->via1.size;
size=msg->via1.hdr-buf;
|
efeaaf53 |
DBG("via len: %d, initial size: %d\n", via_len, size);
|
888ca09d |
if (msg->via1.next){
|
e60a9728 |
/* keep hdr =substract hdr size +1 (hdr':') and add
*/
via_len-=strlen(msg->via1.hdr)+1;
|
888ca09d |
size+=strlen(msg->via1.hdr)+1;
|
efeaaf53 |
DBG(" adjusted via len: %d, initial size: %d\n",
|
413d69f1 |
via_len, size);
|
888ca09d |
}
|
413d69f1 |
new_len=len-via_len;
|
efeaaf53 |
DBG(" old size: %d, new size: %d\n", len, new_len);
|
413d69f1 |
new_buf=(char*)malloc(new_len+1);/* +1 is for debugging (\0 to print it )*/
|
888ca09d |
if (new_buf==0){
|
efeaaf53 |
LOG(L_ERR, "ERROR: forward_reply: out of memory\n");
|
888ca09d |
goto error;
}
|
413d69f1 |
new_buf[new_len]=0; /* debug: print the message */
|
888ca09d |
memcpy(new_buf, orig, size);
offset=size;
s_offset=size+via_len;
memcpy(new_buf+offset,orig+s_offset, len-s_offset);
/* send it! */
|
efeaaf53 |
DBG(" copied size: orig:%d, new: %d, rest: %d\n",
|
413d69f1 |
s_offset, offset,
len-s_offset );
|
efeaaf53 |
DBG("Sending: to %s:%d, \n%s.\n",
|
888ca09d |
msg->via2.host,
(unsigned short)msg->via2.port,
new_buf);
|
e60a9728 |
/* fork? gethostbyname will probably block... */
he=gethostbyname(msg->via2.host);
if (he==0){
|
efeaaf53 |
LOG(L_NOTICE, "ERROR:forward_reply:gethostbyname(%s) failure\n",
msg->via2.host);
|
e60a9728 |
goto error;
}
|
dc862225 |
to->sin_family = AF_INET;
to->sin_port = (msg->via2.port)?htons(msg->via2.port):htons(SIP_PORT);
to->sin_addr.s_addr=*((long*)he->h_addr_list[0]);
|
5b253cc6 |
|
e60a9728 |
|
0a974a1d |
#ifdef STATS
|
5b253cc6 |
stats.total_tx++;
|
0a974a1d |
#endif
|
3e429f5c |
if (udp_send(new_buf,new_len, (struct sockaddr*) to,
sizeof(struct sockaddr_in))==-1)
|
e60a9728 |
goto error;
|
0a974a1d |
#ifdef STATS
|
5b253cc6 |
else stats.ok_tx_rs++;
|
0a974a1d |
#endif
|
888ca09d |
|
e60a9728 |
free(new_buf);
|
dc862225 |
free(to);
|
888ca09d |
return 0;
error:
|
e60a9728 |
if (new_buf) free(new_buf);
|
dc862225 |
if (to) free(to);
|
888ca09d |
return -1;
}
|