/* * $Id$ */ #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> #include "forward.h" #include "config.h" #include "msg_parser.h" #include "route.h" #include "dprint.h" #include "udp_server.h" #include "globals.h" #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 /* 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 */ if (strcmp(name, inet_ntoa( *(struct in_addr *)&ip ))==0) return 0; if (resolver&DO_DNS){ /* try all names ips */ he=gethostbyname(name); for(i=0; he->h_addr_list[i];i++){ if (*(unsigned long*)he->h_addr_list[i]==ip) return 0; } } if (resolver&DO_REV_DNS){ /* try reverse dns */ he=gethostbyaddr((char*)&ip, sizeof(ip), AF_INET); if (strcmp(he->h_name, name)==0) return 0; for (i=0; he->h_aliases[i];i++){ if (strcmp(he->h_aliases[i],name)==0) return 0; } } return -1; } int forward_request(char * orig, char* buf, unsigned int len, struct sip_msg* msg, struct route_elem* re, unsigned long source_ip) { unsigned int new_len, via_len, received_len; char line_buf[MAX_VIA_LINE_SIZE]; char received_buf[MAX_RECEIVED_SIZE]; char* new_buf; int offset, s_offset, size; struct sockaddr_in* to; received_len=0; new_buf=0; to=0; to=(struct sockaddr_in*)malloc(sizeof(struct sockaddr)); if (to==0){ LOG(L_ERR, "ERROR: forward_reply: out of memory\n"); goto error; } via_len=snprintf(line_buf, MAX_VIA_LINE_SIZE, "Via: SIP/2.0/UDP %s:%d\r\n", names[0], port_no); /* check if received needs to be added */ if (check_address(source_ip, msg->via1.host, received_dns)!=0){ received_len=snprintf(received_buf, MAX_RECEIVED_SIZE, ";received=%s", inet_ntoa(*(struct in_addr *)&source_ip)); } new_len=len+via_len+received_len; new_buf=(char*)malloc(new_len+1); if (new_buf==0){ LOG(L_ERR, "ERROR: forward_request: out of memory\n"); goto error; } /* copy msg till first via */ offset=s_offset=0; size=msg->via1.hdr-buf; memcpy(new_buf, orig, size); offset+=size; s_offset+=size; /* add our via */ memcpy(new_buf+offset, line_buf, via_len); offset+=via_len; /* modify original via if neccesarry (received=...)*/ 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 ':'*/ } } memcpy(new_buf+offset, orig+s_offset, size); offset+=size; s_offset+=size; memcpy(new_buf+offset, received_buf, received_len); offset+=received_len; } /* copy the rest of the msg */ memcpy(new_buf+offset, orig+s_offset, len-s_offset); new_buf[new_len]=0; /* send it! */ DBG("Sending:\n%s.\n", new_buf); DBG("orig. len=%d, new_len=%d, via_len=%d, received_len=%d\n", len, new_len, via_len, received_len); to->sin_family = AF_INET; to->sin_port = (re->port)?htons(re->port):htons(SIP_PORT); /* if error try next ip address if possible */ if (re->ok==0){ if (re->host.h_addr_list[re->current_addr_idx+1]) re->current_addr_idx++; re->ok=1; } /* ? not 64bit clean?*/ to->sin_addr.s_addr=*((long*)re->host.h_addr_list[re->current_addr_idx]); re->tx++; re->tx_bytes+=new_len; if (udp_send(new_buf, new_len, to, sizeof(struct sockaddr))==-1){ re->errors++; re->ok=0; goto error; } free(new_buf); free(to); return 0; error: if (new_buf) free(new_buf); if (to) free(to); return -1; } /* removes first via & sends msg to the second */ int forward_reply(char * orig, char* buf, unsigned int len, struct sip_msg* msg) { unsigned int new_len, via_len,r; char* new_buf; int offset, s_offset, size; struct hostent* he; struct sockaddr_in* to; new_buf=0; to=0; to=(struct sockaddr_in*)malloc(sizeof(struct sockaddr)); if (to==0){ LOG(L_ERR, "ERROR: forward_reply: out of memory\n"); goto error; } /*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){ LOG(L_NOTICE, "ERROR: forward_reply: host in first via!=me : %s\n", msg->via1.host); /* send error msg back? */ goto error; } } /* we must remove the first via */ via_len=msg->via1.size; size=msg->via1.hdr-buf; DBG("via len: %d, initial size: %d\n", via_len, size); if (msg->via1.next){ /* keep hdr =substract hdr size +1 (hdr':') and add */ via_len-=strlen(msg->via1.hdr)+1; size+=strlen(msg->via1.hdr)+1; DBG(" adjusted via len: %d, initial size: %d\n", via_len, size); } new_len=len-via_len; DBG(" old size: %d, new size: %d\n", len, new_len); new_buf=(char*)malloc(new_len+1);/* +1 is for debugging (\0 to print it )*/ if (new_buf==0){ LOG(L_ERR, "ERROR: forward_reply: out of memory\n"); goto error; } new_buf[new_len]=0; /* debug: print the message */ memcpy(new_buf, orig, size); offset=size; s_offset=size+via_len; memcpy(new_buf+offset,orig+s_offset, len-s_offset); /* send it! */ DBG(" copied size: orig:%d, new: %d, rest: %d\n", s_offset, offset, len-s_offset ); DBG("Sending: to %s:%d, \n%s.\n", msg->via2.host, (unsigned short)msg->via2.port, new_buf); /* fork? gethostbyname will probably block... */ he=gethostbyname(msg->via2.host); if (he==0){ LOG(L_NOTICE, "ERROR:forward_reply:gethostbyname(%s) failure\n", msg->via2.host); goto error; } 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]); if (udp_send(new_buf,new_len, to, sizeof(struct sockaddr))==-1) goto error; free(new_buf); free(to); return 0; error: if (new_buf) free(new_buf); if (to) free(to); return -1; }