shoot.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. shot written by ashhar farhan, is not bound by any licensing at all.
  3. you are free to use this code as you deem fit. Just don't blame the author
  4. for any problems you may have using it.
  5. bouquets and brickbats to [email protected]
  6. */
  7. /* changes by [email protected]; now messages can be really received;
  8. status code returned is 2 for some local errors , 0 for success
  9. and 1 for remote error -- ICMP/timeout; can be used to test if
  10. a server is alive; 1xx messages are now ignored; windows support
  11. dropped
  12. */
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #include <sys/types.h>
  16. #include <sys/time.h>
  17. #include <string.h>
  18. #include <ctype.h>
  19. #include <time.h>
  20. #include <unistd.h>
  21. #include <netdb.h>
  22. #include <sys/socket.h>
  23. #include <regex.h>
  24. regex_t* regexp;
  25. #define RESIZE 1024
  26. /* take either a dot.decimal string of ip address or a
  27. domain name and returns a NETWORK ordered long int containing
  28. the address. i chose to internally represent the address as long for speedier
  29. comparisons.
  30. any changes to getaddress have to be patched back to the net library.
  31. contact: [email protected]
  32. returns zero if there is an error.
  33. this is convenient as 0 means 'this' host and the traffic of
  34. a badly behaving dns system remains inside (you send to 0.0.0.0)
  35. */
  36. long getaddress(char *host)
  37. {
  38. int i, dotcount=0;
  39. char *p = host;
  40. struct hostent* pent;
  41. long l, *lp;
  42. /*try understanding if this is a valid ip address
  43. we are skipping the values of the octets specified here.
  44. for instance, this code will allow 952.0.320.567 through*/
  45. while (*p)
  46. {
  47. for (i = 0; i < 3; i++, p++)
  48. if (!isdigit(*p))
  49. break;
  50. if (*p != '.')
  51. break;
  52. p++;
  53. dotcount++;
  54. }
  55. /* three dots with up to three digits in before, between and after ? */
  56. if (dotcount == 3 && i > 0 && i <= 3)
  57. return inet_addr(host);
  58. /* try the system's own resolution mechanism for dns lookup:
  59. required only for domain names.
  60. inspite of what the rfc2543 :D Using SRV DNS Records recommends,
  61. we are leaving it to the operating system to do the name caching.
  62. this is an important implementation issue especially in the light
  63. dynamic dns servers like dynip.com or dyndns.com where a dial
  64. ip address is dynamically assigned a sub domain like farhan.dynip.com
  65. although expensive, this is a must to allow OS to take
  66. the decision to expire the DNS records as it deems fit.
  67. */
  68. pent = gethostbyname(host);
  69. if (!pent) {
  70. perror("no gethostbyname");
  71. exit(2);
  72. }
  73. lp = (long *) (pent->h_addr);
  74. l = *lp;
  75. return l;
  76. }
  77. /*
  78. shoot:
  79. takes:
  80. 1. the text message of buff to
  81. 2. the address (network ordered byte order)
  82. 3. and port (not network byte ordered).
  83. starting from half a second, times-out on replies and
  84. keeps retrying with exponential back-off that flattens out
  85. at 5 seconds (5000 milliseconds).
  86. * Does not stop sending unless a final response is received.
  87. we are detecting the final response without a '1' as the first
  88. letter.
  89. */
  90. void shoot(char *buff, long address, int lport, int rport )
  91. {
  92. struct sockaddr_in addr;
  93. /* jku - b server structures */
  94. struct sockaddr_in sockname;
  95. int ssock;
  96. /*
  97. char compiledre[ RESIZE ];
  98. */
  99. /* jku - e */
  100. int retryAfter = 500, i, len, ret;
  101. int nretries = 10;
  102. int sock;
  103. struct timeval tv;
  104. fd_set fd;
  105. char reply[1600];
  106. /* create a socket */
  107. sock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  108. if (sock==-1) {
  109. perror("no client socket");
  110. exit(2);
  111. }
  112. /* jku - b */
  113. ssock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  114. if (sock==-1) {
  115. perror("no server socket");
  116. exit(2);
  117. }
  118. sockname.sin_family=AF_INET;
  119. sockname.sin_addr.s_addr = htonl( INADDR_ANY );
  120. sockname.sin_port = htons((short)lport);
  121. if (bind( ssock, (struct sockaddr *) &sockname, sizeof(sockname) )==-1) {
  122. perror("no bind");
  123. exit(2);
  124. }
  125. /* should capture: SIP/2.0 100 Trying */
  126. /* compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0'); */
  127. regexp=(regex_t*)malloc(sizeof(regex_t));
  128. regcomp(regexp, "^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE);
  129. /* jku - e */
  130. addr.sin_addr.s_addr = address;
  131. addr.sin_port = htons((short)rport);
  132. addr.sin_family = AF_INET;
  133. /* we connect as per the RFC 2543 recommendations
  134. modified from sendto/recvfrom */
  135. ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
  136. if (ret==-1) {
  137. perror("no connect");
  138. exit(2);
  139. }
  140. /* jku - e */
  141. for (i = 0; i < nretries; i++)
  142. {
  143. puts("/* request */");
  144. puts(buff);
  145. putchar('\n');
  146. ret = send(sock, buff, strlen(buff), 0);
  147. if (ret==-1) {
  148. perror("send failure");
  149. exit( 1 );
  150. }
  151. tv.tv_sec = retryAfter/1000;
  152. tv.tv_usec = (retryAfter % 1000) * 1000;
  153. FD_ZERO(&fd);
  154. FD_SET(ssock, &fd);
  155. /* TO-DO: there does appear to be a problem with this select returning a zero
  156. even when there is data pending in the recv queue.
  157. please help, someone! */
  158. ret = select(6, &fd, NULL, NULL, &tv);
  159. if (ret == 0)
  160. {
  161. puts("\n/* timeout */\n");
  162. retryAfter = retryAfter * 2;
  163. if (retryAfter > 5000)
  164. retryAfter = 5000;
  165. /* we should have retrieved the error code and displayed
  166. we are not doing that because there is a great variation
  167. in the process of retrieving error codes between
  168. micro$oft and *nix world*/
  169. continue;
  170. } else if ( ret == -1 ) {
  171. perror("select error");
  172. exit(2);
  173. } /* no timeout, no error ... something has happened :-) */
  174. else if (FD_ISSET(ssock, &fd)) {
  175. puts ("\nmessage received\n");
  176. } else {
  177. puts("\nselect returned successfully, nothing received\n");
  178. continue;
  179. }
  180. /* we are retrieving only the extend of a decent MSS = 1500 bytes */
  181. len = sizeof(addr);
  182. ret = recv(ssock, reply, 1500, 0);
  183. if(ret > 0)
  184. {
  185. reply[ret] = 0;
  186. puts("/* reply */");
  187. puts(reply);
  188. putchar('\n');
  189. /* if (step( reply, compiledre )) { */
  190. if (regexec((regex_t*)regexp, reply, 0, 0, 0)==0) {
  191. puts(" provisional received; still waiting for a final response\n ");
  192. continue;
  193. } else {
  194. puts(" final received; congratulations!\n ");
  195. exit(0);
  196. }
  197. }
  198. else {
  199. perror("recv error");
  200. exit(2);
  201. }
  202. }
  203. /* after all the retries, nothing has come back :-( */
  204. puts("/* I give up retransmission....");
  205. exit(1);
  206. }
  207. int main(int argc, char *argv[])
  208. {
  209. long address;
  210. FILE *pf;
  211. char buff[1600];
  212. int length;
  213. int lport=0;
  214. int rport=5060;
  215. if (! (argc >= 3 && argc <= 5))
  216. {
  217. puts("usage: shoot file host [rport] [lport]");
  218. exit(2);
  219. }
  220. address = getaddress(argv[2]);
  221. if (!address)
  222. {
  223. puts("error:unable to determine the remote host address.");
  224. exit(2);
  225. }
  226. /* take the port as 5060 even if it is incorrectly specified */
  227. if (argc >= 4)
  228. {
  229. rport = atoi(argv[3]);
  230. if (!rport) {
  231. puts("error: non-numerical remote port number");
  232. exit(1);
  233. }
  234. if (argc==5) {
  235. lport=atoi(argv[4]);
  236. if (!lport) {
  237. puts("error: non-numerical local port number");
  238. exit(1);
  239. }
  240. }
  241. }
  242. /* file is opened in binary mode so that the cr-lf is preserved */
  243. pf = fopen(argv[1], "rb");
  244. if (!pf)
  245. {
  246. puts("unable to open the file.\n");
  247. return 1;
  248. }
  249. length = fread(buff, 1, sizeof(buff), pf);
  250. if (length >= sizeof(buff))
  251. {
  252. puts("error:the file is too big. try files of less than 1500 bytes.");
  253. return 1;
  254. }
  255. fclose(pf);
  256. buff[length] = 0;
  257. shoot(buff, address, lport, rport );
  258. /* visual studio closes the debug console as soon as the
  259. program terminates. This is to hold the window from collapsing
  260. Uncomment it if needed.
  261. getchar();*/
  262. return 0;
  263. }
  264. /*
  265. shoot will exercise all the types of sip servers.
  266. it is not to be used to measure round-trips and general connectivity.
  267. use ping for that.
  268. written by farhan on 10th august, 2000.
  269. TO-DO:
  270. 1. replace the command line arguments with just a sip url like this:
  271. shoot invite.txt sip:[email protected]:5060
  272. 2. understand redirect response and retransmit to the redirected server.
  273. */