Browse code

shoot utility added; sends requests with SIP retransmission until reply appears

Jiri Kuthan authored on 14/11/2001 06:06:01
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,345 @@
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 dont blame the author
4
+for any problems you may have using it.
5
+bouquets and brickbats to farhan@hotfoon.com
6
+*/
7
+
8
+/* changes by jiri@iptel.org; now messages can be really received;
9
+   status code returned is 2 for some local errors , 0 for success
10
+   and 1 for remote error -- ICMP/timeout; can be used to test if
11
+   a server is alive; 1xx messages are now ignored
12
+*/
13
+
14
+/* currently, compiles only for Solaris; Linux returns
15
+ /usr/include/regexp.h:131: cannot convert `char *' to `unsigned char *' 
16
+*/
17
+
18
+int 		regerr;
19
+
20
+#define INIT         register char *sp = instring;
21
+#define GETC()       (*sp++)
22
+#define PEEKC()      (*sp)
23
+#define UNGETC(c)    (--sp)
24
+/*#define RETURN(*c)    return; */
25
+#define RETURN(c)    return c;
26
+#define ERROR(c)     regerr
27
+#include <regexp.h>
28
+
29
+
30
+#include <stdio.h>
31
+#include <string.h>
32
+#include <ctype.h>
33
+#include <time.h>
34
+#include <sys/types.h>
35
+#include <stdlib.h>
36
+/* windows specific headers */
37
+#ifdef WIN32
38
+#include <windows.h>
39
+#include <winsock.h>
40
+#define close(a) closesocket(a)
41
+#else
42
+/* *nix specific networking headers */
43
+#include <sys/time.h>
44
+#include <unistd.h>
45
+#include <netdb.h>
46
+#include <arpa/inet.h>
47
+#include <sys/socket.h>
48
+#include <netinet/in.h>
49
+#endif
50
+
51
+#define RESIZE		1024
52
+
53
+/* take either a dot.decimal string of ip address or a 
54
+domain name and returns a NETWORK ordered long int containing
55
+the address. i chose to internally represent the address as long for speedier
56
+comparisions.
57
+
58
+any changes to getaddress have to be patched back to the net library.
59
+contact: farhan@hotfoon.com
60
+
61
+  returns zero if there is an error.
62
+  this is convenient as 0 means 'this' host and the traffic of
63
+  a badly behaving dns system remains inside (you send to 0.0.0.0)
64
+*/
65
+
66
+long getaddress(char *host)
67
+{
68
+	int i, dotcount=0;
69
+	char *p = host;
70
+	struct hostent		*pent;
71
+	/* struct sockaddr_in	addr; */ /* see the note on portabilit at the end of the routine */
72
+
73
+	/*try understanding if this is a valid ip address
74
+	we are skipping the values of the octets specified here.
75
+	for instance, this code will allow 952.0.320.567 through*/
76
+	while (*p)
77
+	{
78
+		for (i = 0; i < 3; i++, p++)
79
+			if (!isdigit(*p))
80
+				break;
81
+		if (*p != '.')
82
+			break;
83
+		p++;
84
+		dotcount++;
85
+	}
86
+
87
+	/* three dots with upto three digits in before, between and after ? */
88
+	if (dotcount == 3 && i > 0 && i <= 3)
89
+		return inet_addr(host);
90
+
91
+	/* try the system's own resolution mechanism for dns lookup:
92
+	 required only for domain names.
93
+	 inspite of what the rfc2543 :D Using SRV DNS Records recommends,
94
+	 we are leaving it to the operating system to do the name caching.
95
+
96
+	 this is an important implementational issue especially in the light
97
+	 dynamic dns servers like dynip.com or dyndns.com where a dial
98
+	 ip address is dynamically assigned a sub domain like farhan.dynip.com
99
+
100
+	 although expensive, this is a must to allow OS to take
101
+	 the decision to expire the DNS records as it deems fit.
102
+	*/
103
+	pent = gethostbyname(host);
104
+	if (!pent) {
105
+		perror("no gethostbyname");
106
+		exit(2);
107
+	}
108
+
109
+	/* PORTABILITY-ISSUE: replacing a costly memcpy call with a hack, may not work on 
110
+	some systems.  
111
+	memcpy(&addr.sin_addr, (pent->h_addr), pent->h_length);
112
+	return addr.sin_addr.s_addr; */
113
+	return *((long *)(pent->h_addr));
114
+}
115
+
116
+
117
+/*
118
+shoot:
119
+takes:
120
+	1. the text message of buff to 
121
+	2. the address (network orderd byte order)
122
+	3. and port (not network byte ordered).
123
+
124
+starting from half a second, times-out on replies and
125
+keeps retrying with exponential back-off that flattens out
126
+at 5 seconds (5000 milliseconds).
127
+
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 port)
133
+{
134
+	struct sockaddr_in	addr;
135
+	/* jku - b  server structures */
136
+	struct sockaddr_in	sockname;
137
+	int ssock;
138
+	char compiledre[ RESIZE ];
139
+	/* jku - e */
140
+	int retryAfter = 500, i, len, ret;
141
+	int	nretries = 10;
142
+	int	sock;
143
+	timeval	tv;
144
+	fd_set	fd;
145
+	char	reply[1600];
146
+
147
+	/* create a socket */
148
+	sock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
149
+	if (sock==-1) {
150
+		perror("no client socket");
151
+		exit(2);
152
+	}
153
+
154
+	/* jku - b */
155
+	ssock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
156
+	if (sock==-1) {
157
+		perror("no server socket");
158
+		exit(2);
159
+	}
160
+
161
+	sockname.sin_family=AF_INET;
162
+	sockname.sin_addr.s_addr = htonl( INADDR_ANY );
163
+	sockname.sin_port = htons((short)port);
164
+	if (bind( ssock, (sockaddr *) &sockname, sizeof(sockname) )==-1) {
165
+		perror("no bind");
166
+		exit(2);
167
+	}
168
+
169
+	/* should capture: SIP/2.0 100 Trying */
170
+	compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0');
171
+	
172
+
173
+	/* jku - e */
174
+
175
+	addr.sin_addr.s_addr = address;
176
+	addr.sin_port = htons((short)port);
177
+	addr.sin_family = AF_INET;
178
+	
179
+	/* we connect as per the RFC 2543 recommendations
180
+	modified from sendto/recvfrom */
181
+
182
+	ret = connect(sock, (sockaddr *)&addr, sizeof(addr));
183
+	if (ret==-1) {
184
+		perror("no connect");
185
+		exit(2);
186
+	}
187
+	/* jku - e */
188
+
189
+	for (i = 0; i < nretries; i++)
190
+	{
191
+		puts("/* request */");
192
+		puts(buff);
193
+		putchar('\n');
194
+
195
+		ret = send(sock, buff, strlen(buff), 0);
196
+		if (ret==-1) {
197
+			perror("send failure");
198
+			exit( 1 );
199
+		}
200
+		
201
+
202
+		tv.tv_sec = retryAfter/1000;
203
+		tv.tv_usec = (retryAfter % 1000) * 1000;
204
+
205
+		FD_ZERO(&fd);
206
+		FD_SET(ssock, &fd); 
207
+
208
+		/* TO-DO: there does appear to be a problem with this select returning a zero
209
+		even when there is data pending in the recv queue. 
210
+		please help, someone! */
211
+
212
+		ret = select(6, &fd, NULL, NULL, &tv);
213
+		if (ret == 0)
214
+		{
215
+			puts("\n/* timeout */\n");
216
+			retryAfter = retryAfter * 2;
217
+			if (retryAfter > 5000)
218
+				retryAfter = 5000;
219
+			/* we should have retrieved the error code and displayed
220
+			we are not doing that because there is a great variation
221
+			in the process of retrieveing error codes between
222
+			micro$oft and *nix world*/
223
+			continue;
224
+		} else if ( ret == -1 ) {
225
+			perror("select error");
226
+			exit(2);
227
+		} /* no timeout, no error ... something has happened :-) */
228
+                 else if (FD_ISSET(ssock, &fd)) {
229
+			puts ("\nmessage received\n");
230
+		} else {
231
+			puts("\nselect returned succesfuly, nothing received\n");
232
+			continue;
233
+		}
234
+
235
+		/* we are retrieving only the extend of a decent MSS = 1500 bytes */
236
+		len = sizeof(addr);
237
+		ret = recv(ssock, reply, 1500, 0);
238
+		if(ret > 0)
239
+		{
240
+			reply[ret] = 0;
241
+			puts("/* reply */");
242
+			puts(reply);
243
+			putchar('\n');
244
+			if (step( reply, compiledre )) {
245
+				puts(" provisional received; still waiting for a final response\n ");
246
+				continue;
247
+			} else {
248
+				puts(" final received; congratulations!\n ");
249
+				exit(0);
250
+			}
251
+		
252
+		} 
253
+		else	{
254
+			perror("recv error");
255
+			exit(2);
256
+			}
257
+	}
258
+	/* after all the retries, nothing has come back :-( */
259
+	puts("/* I give up retransmission....");
260
+	exit(1);
261
+}
262
+
263
+int main(int argc, char *argv[])
264
+{
265
+	long	address;
266
+	FILE	*pf;
267
+	char	buff[1600];
268
+	int		length;
269
+	int		port;
270
+#ifdef WIN32
271
+	WSADATA	wsadata;
272
+	int err = WSAStartup(0x0101, &wsadata);
273
+	if (err != 0)
274
+	{
275
+		printf("shoot cannot be used as TCP/IP is not available.\n");
276
+		exit(0);
277
+	}
278
+#endif
279
+
280
+
281
+	if (argc != 3 && argc != 4)
282
+	{
283
+		puts("usage: shoot file host [port]");
284
+		exit(2);
285
+	}
286
+
287
+	address = getaddress(argv[2]);
288
+	if (!address)
289
+	{
290
+		puts("error:unable to determine the remote host address.");
291
+		exit(2);
292
+	}
293
+
294
+	/* take the port as 5060 even if it is incorrectly specified */
295
+	if (argc == 4)
296
+	{
297
+		port = atoi(argv[3]);
298
+		if (!port)
299
+			port = 5060;
300
+	}
301
+	else
302
+		port = 5060;
303
+
304
+	/* file is opened in binary mode so that the cr-lf is preserved */
305
+	pf = fopen(argv[1], "rb");
306
+	if (!pf)
307
+	{
308
+		puts("unable to open the file.\n");
309
+		return 1;
310
+	}
311
+	length  = fread(buff, 1, sizeof(buff), pf);
312
+	if (length >= sizeof(buff))
313
+	{
314
+		puts("error:the file is too big. try files of less than 1500 bytes.");
315
+		return 1;
316
+	}
317
+	fclose(pf);
318
+	buff[length] = 0;
319
+
320
+	shoot(buff, address, port);
321
+
322
+	/* visual studio closes the debug console as soon as the 
323
+	program terminates. this is to hold the window from collapsing
324
+	Uncomment it if needed.
325
+	getchar();*/
326
+	
327
+
328
+	return 0;
329
+}
330
+
331
+
332
+/*
333
+shoot will exercise the all types of sip servers.
334
+it is not to be used to measure round-trips and general connectivity.
335
+use ping for that. 
336
+written by farhan on 10th august, 2000.
337
+
338
+TO-DO:
339
+1. replace the command line arguments with just a sip url like this:
340
+	shoot invite.txt sip:farhan@sip.hotfoon.com:5060
341
+
342
+2. understand redirect response and retransmit to the redirected server.
343
+
344
+*/
345
+