mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
7.7 KiB
326 lines
7.7 KiB
/*
|
|
shot written by ashhar farhan, is not bound by any licensing at all.
|
|
you are free to use this code as you deem fit. just dont blame the author
|
|
for any problems you may have using it.
|
|
bouquets and brickbats to farhan@hotfoon.com
|
|
*/
|
|
|
|
|
|
/* changes by jiri@iptel.org; now messages can be really received;
|
|
status code returned is 2 for some local errors , 0 for success
|
|
and 1 for remote error -- ICMP/timeout; can be used to test if
|
|
a server is alive; 1xx messages are now ignored; windows support
|
|
dropped
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <regex.h>
|
|
regex_t* regexp;
|
|
|
|
#define RESIZE 1024
|
|
|
|
/* take either a dot.decimal string of ip address or a
|
|
domain name and returns a NETWORK ordered long int containing
|
|
the address. i chose to internally represent the address as long for speedier
|
|
comparisons.
|
|
|
|
any changes to getaddress have to be patched back to the net library.
|
|
contact: farhan@hotfoon.com
|
|
|
|
returns zero if there is an error.
|
|
this is convenient as 0 means 'this' host and the traffic of
|
|
a badly behaving dns system remains inside (you send to 0.0.0.0)
|
|
*/
|
|
|
|
long getaddress(char *host)
|
|
{
|
|
int i, dotcount=0;
|
|
char *p = host;
|
|
struct hostent* pent;
|
|
long l, *lp;
|
|
|
|
/*try understanding if this is a valid ip address
|
|
we are skipping the values of the octets specified here.
|
|
for instance, this code will allow 952.0.320.567 through*/
|
|
while (*p)
|
|
{
|
|
for (i = 0; i < 3; i++, p++)
|
|
if (!isdigit(*p))
|
|
break;
|
|
if (*p != '.')
|
|
break;
|
|
p++;
|
|
dotcount++;
|
|
}
|
|
|
|
/* three dots with up to three digits in before, between and after ? */
|
|
if (dotcount == 3 && i > 0 && i <= 3)
|
|
return inet_addr(host);
|
|
|
|
/* try the system's own resolution mechanism for dns lookup:
|
|
required only for domain names.
|
|
inspite of what the rfc2543 :D Using SRV DNS Records recommends,
|
|
we are leaving it to the operating system to do the name caching.
|
|
|
|
this is an important implementation issue especially in the light
|
|
dynamic dns servers like dynip.com or dyndns.com where a dial
|
|
ip address is dynamically assigned a sub domain like farhan.dynip.com
|
|
|
|
although expensive, this is a must to allow OS to take
|
|
the decision to expire the DNS records as it deems fit.
|
|
*/
|
|
pent = gethostbyname(host);
|
|
if (!pent) {
|
|
perror("no gethostbyname");
|
|
exit(2);
|
|
}
|
|
|
|
lp = (long *) (pent->h_addr);
|
|
l = *lp;
|
|
return l;
|
|
}
|
|
|
|
|
|
/*
|
|
shoot:
|
|
takes:
|
|
1. the text message of buff to
|
|
2. the address (network ordered byte order)
|
|
3. and port (not network byte ordered).
|
|
|
|
starting from half a second, times-out on replies and
|
|
keeps retrying with exponential back-off that flattens out
|
|
at 5 seconds (5000 milliseconds).
|
|
|
|
* Does not stop sending unless a final response is received.
|
|
we are detecting the final response without a '1' as the first
|
|
letter.
|
|
*/
|
|
void shoot(char *buff, long address, int lport, int rport )
|
|
{
|
|
struct sockaddr_in addr;
|
|
/* jku - b server structures */
|
|
struct sockaddr_in sockname;
|
|
int ssock;
|
|
/*
|
|
char compiledre[ RESIZE ];
|
|
*/
|
|
/* jku - e */
|
|
int retryAfter = 500, i, len, ret;
|
|
int nretries = 10;
|
|
int sock;
|
|
struct timeval tv;
|
|
fd_set fd;
|
|
char reply[1600];
|
|
|
|
/* create a socket */
|
|
sock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (sock==-1) {
|
|
perror("no client socket");
|
|
exit(2);
|
|
}
|
|
|
|
/* jku - b */
|
|
ssock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (sock==-1) {
|
|
perror("no server socket");
|
|
exit(2);
|
|
}
|
|
|
|
sockname.sin_family=AF_INET;
|
|
sockname.sin_addr.s_addr = htonl( INADDR_ANY );
|
|
sockname.sin_port = htons((short)lport);
|
|
if (bind( ssock, (struct sockaddr *) &sockname, sizeof(sockname) )==-1) {
|
|
perror("no bind");
|
|
exit(2);
|
|
}
|
|
|
|
/* should capture: SIP/2.0 100 Trying */
|
|
/* compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0'); */
|
|
regexp=(regex_t*)malloc(sizeof(regex_t));
|
|
regcomp(regexp, "^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE);
|
|
|
|
|
|
/* jku - e */
|
|
|
|
addr.sin_addr.s_addr = address;
|
|
addr.sin_port = htons((short)rport);
|
|
addr.sin_family = AF_INET;
|
|
|
|
/* we connect as per the RFC 2543 recommendations
|
|
modified from sendto/recvfrom */
|
|
|
|
ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
|
|
if (ret==-1) {
|
|
perror("no connect");
|
|
exit(2);
|
|
}
|
|
/* jku - e */
|
|
|
|
for (i = 0; i < nretries; i++)
|
|
{
|
|
puts("/* request */");
|
|
puts(buff);
|
|
putchar('\n');
|
|
|
|
ret = send(sock, buff, strlen(buff), 0);
|
|
if (ret==-1) {
|
|
perror("send failure");
|
|
exit( 1 );
|
|
}
|
|
|
|
|
|
tv.tv_sec = retryAfter/1000;
|
|
tv.tv_usec = (retryAfter % 1000) * 1000;
|
|
|
|
FD_ZERO(&fd);
|
|
FD_SET(ssock, &fd);
|
|
|
|
/* TO-DO: there does appear to be a problem with this select returning a zero
|
|
even when there is data pending in the recv queue.
|
|
please help, someone! */
|
|
|
|
ret = select(6, &fd, NULL, NULL, &tv);
|
|
if (ret == 0)
|
|
{
|
|
puts("\n/* timeout */\n");
|
|
retryAfter = retryAfter * 2;
|
|
if (retryAfter > 5000)
|
|
retryAfter = 5000;
|
|
/* we should have retrieved the error code and displayed
|
|
we are not doing that because there is a great variation
|
|
in the process of retrieving error codes between
|
|
micro$oft and *nix world*/
|
|
continue;
|
|
} else if ( ret == -1 ) {
|
|
perror("select error");
|
|
exit(2);
|
|
} /* no timeout, no error ... something has happened :-) */
|
|
else if (FD_ISSET(ssock, &fd)) {
|
|
puts ("\nmessage received\n");
|
|
} else {
|
|
puts("\nselect returned successfully, nothing received\n");
|
|
continue;
|
|
}
|
|
|
|
/* we are retrieving only the extend of a decent MSS = 1500 bytes */
|
|
len = sizeof(addr);
|
|
ret = recv(ssock, reply, 1500, 0);
|
|
if(ret > 0)
|
|
{
|
|
reply[ret] = 0;
|
|
puts("/* reply */");
|
|
puts(reply);
|
|
putchar('\n');
|
|
/* if (step( reply, compiledre )) { */
|
|
if (regexec((regex_t*)regexp, reply, 0, 0, 0)==0) {
|
|
puts(" provisional received; still waiting for a final response\n ");
|
|
continue;
|
|
} else {
|
|
puts(" final received; congratulations!\n ");
|
|
exit(0);
|
|
}
|
|
|
|
}
|
|
else {
|
|
perror("recv error");
|
|
exit(2);
|
|
}
|
|
}
|
|
/* after all the retries, nothing has come back :-( */
|
|
puts("/* I give up retransmission....");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
long address;
|
|
FILE *pf;
|
|
char buff[1600];
|
|
int length;
|
|
int lport=0;
|
|
int rport=5060;
|
|
|
|
if (! (argc >= 3 && argc <= 5))
|
|
{
|
|
puts("usage: shoot file host [rport] [lport]");
|
|
exit(2);
|
|
}
|
|
|
|
address = getaddress(argv[2]);
|
|
if (!address)
|
|
{
|
|
puts("error:unable to determine the remote host address.");
|
|
exit(2);
|
|
}
|
|
|
|
/* take the port as 5060 even if it is incorrectly specified */
|
|
if (argc >= 4)
|
|
{
|
|
rport = atoi(argv[3]);
|
|
if (!rport) {
|
|
puts("error: non-numerical remote port number");
|
|
exit(1);
|
|
}
|
|
if (argc==5) {
|
|
lport=atoi(argv[4]);
|
|
if (!lport) {
|
|
puts("error: non-numerical local port number");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* file is opened in binary mode so that the cr-lf is preserved */
|
|
pf = fopen(argv[1], "rb");
|
|
if (!pf)
|
|
{
|
|
puts("unable to open the file.\n");
|
|
return 1;
|
|
}
|
|
length = fread(buff, 1, sizeof(buff), pf);
|
|
if (length >= sizeof(buff))
|
|
{
|
|
puts("error:the file is too big. try files of less than 1500 bytes.");
|
|
return 1;
|
|
}
|
|
fclose(pf);
|
|
buff[length] = 0;
|
|
|
|
shoot(buff, address, lport, rport );
|
|
|
|
/* visual studio closes the debug console as soon as the
|
|
program terminates. this is to hold the window from collapsing
|
|
Uncomment it if needed.
|
|
getchar();*/
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
shoot will exercise the all types of sip servers.
|
|
it is not to be used to measure round-trips and general connectivity.
|
|
use ping for that.
|
|
written by farhan on 10th august, 2000.
|
|
|
|
TO-DO:
|
|
1. replace the command line arguments with just a sip url like this:
|
|
shoot invite.txt sip:farhan@sip.hotfoon.com:5060
|
|
|
|
2. understand redirect response and retransmit to the redirected server.
|
|
|
|
*/
|
|
|