shoot2.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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 <sys/utsname.h>
  24. #include <regex.h>
  25. regex_t* regexp;
  26. #define RESIZE 1024
  27. #define BUFSIZE 1600
  28. #define VIA_BEGIN_STR "Via: SIP/2.0/UDP "
  29. #define VIA_BEGIN_STR_LEN 17
  30. /* take either a dot.decimal string of ip address or a
  31. domain name and returns a NETWORK ordered long int containing
  32. the address. i chose to internally represent the address as long for speedier
  33. comparisons.
  34. any changes to getaddress have to be patched back to the net library.
  35. contact: [email protected]
  36. returns zero if there is an error.
  37. this is convenient as 0 means 'this' host and the traffic of
  38. a badly behaving dns system remains inside (you send to 0.0.0.0)
  39. */
  40. long getaddress(char *host)
  41. {
  42. int i, dotcount=0;
  43. char *p = host;
  44. struct hostent* pent;
  45. long l, *lp;
  46. /*try understanding if this is a valid ip address
  47. we are skipping the values of the octets specified here.
  48. for instance, this code will allow 952.0.320.567 through*/
  49. while (*p)
  50. {
  51. for (i = 0; i < 3; i++, p++)
  52. if (!isdigit(*p))
  53. break;
  54. if (*p != '.')
  55. break;
  56. p++;
  57. dotcount++;
  58. }
  59. /* three dots with up to three digits in before, between and after ? */
  60. if (dotcount == 3 && i > 0 && i <= 3)
  61. return inet_addr(host);
  62. /* try the system's own resolution mechanism for dns lookup:
  63. required only for domain names.
  64. inspite of what the rfc2543 :D Using SRV DNS Records recommends,
  65. we are leaving it to the operating system to do the name caching.
  66. this is an important implementation issue especially in the light
  67. dynamic dns servers like dynip.com or dyndns.com where a dial
  68. ip address is dynamically assigned a sub domain like farhan.dynip.com
  69. although expensive, this is a must to allow OS to take
  70. the decision to expire the DNS records as it deems fit.
  71. */
  72. pent = gethostbyname(host);
  73. if (!pent) {
  74. perror("no gethostbyname");
  75. exit(2);
  76. }
  77. lp = (long *) (pent->h_addr);
  78. l = *lp;
  79. return l;
  80. }
  81. /* This function tries to add a Via Header Field in the message. */
  82. add_via(char *mes)
  83. {
  84. struct utsname myname;
  85. char *via_line, *via, *backup;
  86. /* get our address, only the first one */
  87. if (uname (&myname) <0){
  88. printf("cannot determine hostname\n");
  89. exit(2);
  90. }
  91. #ifdef DEBUG
  92. printf("determined hostname: %s\n", myname.nodename);
  93. #endif
  94. via_line = malloc(VIA_BEGIN_STR_LEN + strlen(myname.nodename) + 3);
  95. strcat(via_line, VIA_BEGIN_STR);
  96. strcat(via_line, myname.nodename);
  97. strcat(via_line, "\r\n");
  98. #ifdef DEBUG
  99. printf("our Via-Line: %s\n", via_line);
  100. #endif
  101. if (strlen(mes)+strlen(via_line)>= BUFSIZE){
  102. printf("can't add our Via Header Line because file is too big\n");
  103. exit(2);
  104. }
  105. if ((via=strstr(mes,"Via:"))==NULL){
  106. /* We doesn't find a Via so we insert our via
  107. direct after the first line. */
  108. via=strchr(mes,'\n');
  109. via++;
  110. }
  111. backup=malloc(strlen(via));
  112. strncpy(backup, via, strlen(via));
  113. strncpy(via, via_line, strlen(via_line));
  114. strncpy(via+strlen(via_line), backup, strlen(backup));
  115. #ifdef DEBUG
  116. printf("New message:\n%s", mes);
  117. #endif
  118. }
  119. /*
  120. shoot:
  121. takes:
  122. 1. the text message of buff to
  123. 2. the address (network ordered byte order)
  124. 3. and port (not network byte ordered).
  125. starting from half a second, times-out on replies and
  126. keeps retrying with exponential back-off that flattens out
  127. at 5 seconds (5000 milliseconds).
  128. * Does not stop sending unless a final response is received.
  129. we are detecting the final response without a '1' as the first
  130. letter.
  131. */
  132. void shoot(char *buff, long address, int lport, int rport )
  133. {
  134. struct sockaddr_in addr;
  135. /* jku - b server structures */
  136. struct sockaddr_in sockname;
  137. int ssock;
  138. /*
  139. char compiledre[ RESIZE ];
  140. */
  141. /* jku - e */
  142. int retryAfter = 500;
  143. int nretries = 10;
  144. int sock, i, len, ret;
  145. struct timeval tv;
  146. fd_set fd;
  147. char reply[1600];
  148. /* create a socket */
  149. sock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  150. if (sock==-1) {
  151. perror("no client socket");
  152. exit(2);
  153. }
  154. /* jku - b */
  155. ssock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  156. if (ssock==-1) {
  157. perror("no server socket");
  158. exit(2);
  159. }
  160. sockname.sin_family=AF_INET;
  161. sockname.sin_addr.s_addr = htonl( INADDR_ANY );
  162. sockname.sin_port = htons((short)lport);
  163. if (bind( ssock, (struct sockaddr *) &sockname, sizeof(sockname) )==-1) {
  164. perror("no bind");
  165. exit(2);
  166. }
  167. /* should capture: SIP/2.0 100 Trying */
  168. /* compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0'); */
  169. regexp=(regex_t*)malloc(sizeof(regex_t));
  170. regcomp(regexp, "^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE);
  171. /* jku - e */
  172. addr.sin_addr.s_addr = address;
  173. addr.sin_port = htons((short)rport);
  174. addr.sin_family = AF_INET;
  175. /* we connect as per the RFC 2543 recommendations
  176. modified from sendto/recvfrom */
  177. ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
  178. if (ret==-1) {
  179. perror("no connect");
  180. exit(2);
  181. }
  182. /* jku - e */
  183. add_via(buff);
  184. for (i = 0; i < nretries; i++)
  185. {
  186. puts("/* request */");
  187. puts(buff);
  188. putchar('\n');
  189. ret = send(sock, buff, strlen(buff), 0);
  190. if (ret==-1) {
  191. perror("send failure");
  192. exit( 1 );
  193. }
  194. tv.tv_sec = retryAfter/1000;
  195. tv.tv_usec = (retryAfter % 1000) * 1000;
  196. FD_ZERO(&fd);
  197. FD_SET(ssock, &fd);
  198. /* TO-DO: there does appear to be a problem with this select returning a zero
  199. even when there is data pending in the recv queue.
  200. please help, someone! */
  201. ret = select(6, &fd, NULL, NULL, &tv);
  202. if (ret == 0)
  203. {
  204. puts("\n/* timeout */\n");
  205. retryAfter = retryAfter * 2;
  206. if (retryAfter > 5000)
  207. retryAfter = 5000;
  208. /* we should have retrieved the error code and displayed
  209. we are not doing that because there is a great variation
  210. in the process of retrieving error codes between
  211. micro$oft and *nix world*/
  212. continue;
  213. } else if ( ret == -1 ) {
  214. perror("select error");
  215. exit(2);
  216. } /* no timeout, no error ... something has happened :-) */
  217. else if (FD_ISSET(ssock, &fd)) {
  218. puts ("\nmessage received\n");
  219. } else {
  220. puts("\nselect returned successfully, nothing received\n");
  221. continue;
  222. }
  223. /* we are retrieving only the extend of a decent MSS = 1500 bytes */
  224. len = sizeof(addr);
  225. ret = recv(ssock, reply, 1500, 0);
  226. if(ret > 0)
  227. {
  228. reply[ret] = 0;
  229. puts("/* reply */");
  230. puts(reply);
  231. putchar('\n');
  232. /* if (step( reply, compiledre )) { */
  233. if (regexec((regex_t*)regexp, reply, 0, 0, 0)==0) {
  234. puts(" provisional received; still waiting for a final response\n ");
  235. continue;
  236. } else {
  237. puts(" final received; congratulations!\n ");
  238. exit(0);
  239. }
  240. }
  241. else {
  242. perror("recv error");
  243. exit(2);
  244. }
  245. }
  246. /* after all the retries, nothing has come back :-( */
  247. puts("/* I give up retransmission....");
  248. exit(1);
  249. }
  250. int main(int argc, char *argv[])
  251. {
  252. long address;
  253. FILE *pf;
  254. char buff[BUFSIZE];
  255. int length;
  256. int lport=0;
  257. int rport=5060;
  258. char *delim, *delim2;
  259. if (! (argc >= 3 && argc <= 5))
  260. {
  261. puts("usage: shoot file host rport [lport]");
  262. puts("usage: shoot file sip:[user@]hostname[:rport]");
  263. exit(2);
  264. }
  265. /* support for sip:uri added by noh */
  266. if (argc==3){
  267. if ((delim=strchr(argv[2],':'))!=NULL){
  268. delim++;
  269. if (!strncmp(argv[2],"sip",3)){
  270. if ((delim2=strchr(delim,'@'))!=NULL){
  271. /* we don't need the username */
  272. delim2++;
  273. delim=delim2;
  274. }
  275. if ((delim2=strchr(delim,':'))!=NULL){
  276. *delim2 = '\0';
  277. delim2++;
  278. rport = atoi(delim2);
  279. if (!rport) {
  280. puts("error: non-numerical remote port number");
  281. exit(2);
  282. }
  283. }
  284. address = getaddress(delim);
  285. if (!address){
  286. puts("error:unable to determine the remote host address.");
  287. exit(2);
  288. }
  289. }
  290. else{
  291. puts("sip:uri doesn't not begin with sip");
  292. exit(2);
  293. }
  294. }
  295. else{
  296. puts("sip:uri doesn't contain a : ?!");
  297. exit(2);
  298. }
  299. }
  300. else{
  301. address = getaddress(argv[2]);
  302. if (!address){
  303. puts("error:unable to determine the remote host address.");
  304. exit(2);
  305. }
  306. /* take the port as 5060 even if it is incorrectly specified */
  307. if (argc >= 4){
  308. rport = atoi(argv[3]);
  309. if (!rport) {
  310. puts("error: non-numerical remote port number");
  311. exit(2);
  312. }
  313. if (argc==5) {
  314. lport=atoi(argv[4]);
  315. if (!lport) {
  316. puts("error: non-numerical local port number");
  317. exit(2);
  318. }
  319. }
  320. }
  321. }
  322. /* file is opened in binary mode so that the cr-lf is preserved */
  323. pf = fopen(argv[1], "rb");
  324. if (!pf)
  325. {
  326. puts("unable to open the file.\n");
  327. return 1;
  328. }
  329. length = fread(buff, 1, sizeof(buff), pf);
  330. if (length >= sizeof(buff))
  331. {
  332. printf("error:the file is too big. try files of less than %i bytes.\n", BUFSIZE);
  333. puts(" or recompile the program with bigger BUFSIZE defined.");
  334. return 1;
  335. }
  336. fclose(pf);
  337. buff[length] = 0;
  338. shoot(buff, address, lport, rport );
  339. /* visual studio closes the debug console as soon as the
  340. program terminates. This is to hold the window from collapsing
  341. Uncomment it if needed.
  342. getchar();*/
  343. return 0;
  344. }
  345. /*
  346. shoot will exercise all the types of sip servers.
  347. it is not to be used to measure round-trips and general connectivity.
  348. use ping for that.
  349. written by farhan on 10th august, 2000.
  350. TO-DO:
  351. 2. understand redirect response and retransmit to the redirected server.
  352. */