forward.c
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;
 }