123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- 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 don't blame the author
- for any problems you may have using it.
- bouquets and brickbats to [email protected]
- */
- /* changes by [email protected]; 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: [email protected]
- 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 all the 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:[email protected]:5060
- 2. understand redirect response and retransmit to the redirected server.
- */
|