1 /****************************************************************************
2  *                                                                          *
3  * The contents of this file are subject to the WebStone Public License     *
4  * Version 1.0 (the "License"); you may not use this file except in         *
5  * compliance with the License. You may obtain a copy of the License        *
6  * at http://www.mindcraft.com/webstone/license10.html                      *
7  *                                                                          *
8  * Software distributed under the License is distributed on an "AS IS"      *
9  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See      *
10  * the License for the specific language governing rights and limitations   *
11  * under the License.                                                       *
12  *                                                                          *
13  * The Original Code is WebStone 2.5.                                       *
14  *                                                                          *
15  * The Initial Developer of the Original Code is Silicon Graphics, Inc.     *
16  * and Mindcraft, Inc.. Portions created by Silicon Graphics. and           *
17  * Mindcraft. are Copyright (C) 1995-1998 Silicon Graphics, Inc. and        *
18  * Mindcraft, Inc. All Rights Reserved.                                     *
19  *                                                                          *
20  * Contributor(s): ______________________________________.                  *
21  *                                                                          *
22  * @(#) bench.c 2.4@(#)                                                     *
23  ***************************************************************************/
24 
25 
26 /* THIS IS WHERE WE GO OUT AND FETCH A URL */
27 
28 #include <stdio.h>
29 #include <errno.h>
30 
31 #ifndef WIN32
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <sys/socket.h>
35 #include <sys/param.h>
36 #include <netinet/in.h>
37 #include <netdb.h>
38 #endif /* WIN32 */
39 
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #ifdef SUNOS
45 #include <unistd.h>
46 #endif
47 #include <ctype.h>
48 
49 #ifdef WIN32
50 #include <io.h>
51 #include <windows.h>
52 #include <winsock.h>
53 #endif /* WIN32 */
54 
55 #ifdef STONE_SSL
56 #include <openssl/err.h>
57 #include <openssl/ssl.h>
58 #include <openssl/bio.h>
59 #endif
60 
61 #include "sysdep.h"
62 #include "bench.h"
63 #ifdef STONE_SSL
64 #include "random.h"
65 #endif
66 
67 #define ACCEPT_COMMAND "Accept: */* HTTP/1.0\r\n\r\n"
68 #define ACCEPT_COMMAND_LEN 	((int)strlen(ACCEPT_COMMAND))
69 #define MAXCOMMANDLEN	256
70 #define HEADERBUFSIZ	(8*1024)
71 #define SAVE_FILE "savefile"
72 
73 #ifdef STONE_SSL
74 /* globals defined in webclient.c */
75 extern int ssl_client_cache_mode;
76 extern double ssl_mix;
77 extern double ssl_handshake;
78 extern NETPORT ssl_portnum;
79 #endif
80 
81 #ifndef HAVE_STRNCASECMP
82 #define UPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) + 'A' - 'a' : (c))
83 
84 /* compare two strings with max length, ignoring case */
strncasecmp(const char * str1,const char * str2,size_t len)85 int strncasecmp(const char *str1, const char *str2, size_t len) {
86 	int diff;
87 
88     while (*str1 && *str2 && len--) {
89 		if (diff = UPPER(*str1) - UPPER(*str2))
90 			return diff < 0 ? -1 : 1;
91 		str1++;
92 		str2++;
93     }
94     return 0;
95 }
96 #endif /* HAVE_STRNCASECMP */
97 
98 int
99 #ifdef STONE_SSL
get(char * loc,NETPORT port,char * url,rqst_timer_t * timer,char * ssl_version,char * ssl_cipher)100 get(char *loc, NETPORT port, char *url, rqst_timer_t *timer, char* ssl_version,
101 		char* ssl_cipher)
102 #else
103 get(char *loc, NETPORT port, char *url, rqst_timer_t *timer)
104 #endif
105 {
106     SOCKET	sock = BADSOCKET_VALUE;
107     int		writelen;
108     int		bytesread;
109     int		totalbytesread;
110     int		headerlen;
111     int		bodylength;
112     int		contentlength = 0;
113     FILE 	*outputfile = (FILE *)NULL;
114     int		status;
115     char	getcommand[MAXCOMMANDLEN];
116     char	headerbuffer[HEADERBUFSIZ+1];
117     char	*offset;
118     char	outputfilename[MAXPATHLEN];
119     char	version[100];
120     int		count;
121 #ifdef STONE_SSL
122 	static int init = 0;
123 	static SSL_CTX* ctx = NULL;
124 	static SSL* ssl = NULL;
125 	BIO* bio;
126 	int do_ssl = RandomChance(ssl_mix);
127 	int do_handshake = RandomChance(ssl_handshake);
128 	D_PRINTF("do_ssl = %d\n", do_ssl);
129 #endif
130 
131 
132 #ifdef ABORTIVE_CLOSE
133 #error do not enable this option
134     struct linger {
135 		int l_onoff;
136 		int l_linger;
137     } linger_opt;
138 #endif /* ABORTIVE_CLOSE */
139 
140 	TRACE("get(): entering: url='%s'\n", url);
141     if(GETTIMEOFDAY(&timer->entertime, &timer->entertimezone) != 0)
142     {
143 		returnerr("Error retrieving entertime\n");
144 		goto error;
145     }
146     timer->valid = 1;
147 
148     if(GETTIMEOFDAY(&timer->beforeconnect, &timer->beforeconnectzone) != 0)
149     {
150 		returnerr("Error retrieving beforeconnect\n");
151 		goto error;
152     }
153 
154 #ifdef STONE_SSL
155 	if(do_ssl)
156 		sock = connectsock(loc, ssl_portnum, "tcp");
157 	else
158 #endif
159     	sock = connectsock(loc, port, "tcp");
160 
161     if (BADSOCKET(sock))
162     {
163 		D_PRINTF( "Call to connectsock returned %d (%s)\n", sock, neterrstr() );
164                 returnerr("Couldn't connect to WWW server: %s\n", neterrstr());
165                 goto error;
166     }
167 #ifdef STONE_SSL
168 	if(!init) {
169 		ERR_load_crypto_strings();
170       OpenSSL_add_all_algorithms();
171       OpenSSL_add_ssl_algorithms();
172 
173 #ifndef OPENSSL_NO_SSL2
174 		if(ssl_version != NULL && strcasecmp(ssl_version, "SSLv2") == 0) {
175 			if((ctx = SSL_CTX_new(SSLv2_client_method())) == NULL) {
176 				ERR_print_errors_fp(stderr);
177 				goto error;
178 			}
179 			D_PRINTF( "SSL version = SSLv2\n" );
180 		}
181 		else
182 #endif
183 #ifndef OPENSSL_NO_SSL3_METHOD
184 		if(ssl_version != NULL && strcasecmp(ssl_version, "SSLv3") == 0) {
185 			if((ctx = SSL_CTX_new(SSLv3_client_method())) == NULL) {
186 				ERR_print_errors_fp(stderr);
187 				goto error;
188 			}
189 			D_PRINTF( "SSL version = SSLv3\n" );
190 		}
191 		else
192 #endif
193 		{
194 			if((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
195 				ERR_print_errors_fp(stderr);
196 				goto error;
197 			}
198 			D_PRINTF( "SSL version = SSLv23\n" );
199 		}
200 
201 		/* set preferred cipher */
202 		if(ssl_cipher != NULL && strlen(ssl_cipher) > 0) {
203 			SSL_CTX_set_cipher_list(ctx, ssl_cipher);
204 		}
205 
206 		if(ssl_client_cache_mode > 0) {
207 			SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);
208 			/* SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_NO_AUTO_CLEAR);*/
209 		}
210 		D_PRINTF( "SSL client cache mode = %d\n", ssl_client_cache_mode);
211 
212 		if((ssl = SSL_new(ctx)) == NULL) {
213 			SSL_CTX_free(ctx);
214 			ERR_print_errors_fp(stderr);
215 			goto error;
216 		}
217 
218 		init = 1;
219 		D_PRINTF("done init\n");
220 	}
221 
222 	if(do_ssl) {
223 
224 
225 /*
226 		if(do_handshake) {
227 			if(ssl != NULL)
228 				SSL_free(ssl);
229 
230 			if((ssl = SSL_new(ctx)) == NULL) {
231 				SSL_CTX_free(ctx);
232 				ERR_print_errors_fp(stderr);
233 				goto error;
234 			}
235 		}
236 */
237 
238 		if(do_handshake) {
239 			SSL_SESSION_free(ssl->session);
240 			ssl->session = NULL;
241 		}
242 
243 		if((bio = BIO_new_socket(sock, BIO_NOCLOSE)) == NULL) {
244 			ERR_print_errors_fp(stderr);
245 			goto error;
246 		}
247 
248 		SSL_set_bio(ssl, bio, bio);
249 
250 		if(SSL_connect(ssl) <= 0) {
251 			ERR_print_errors_fp(stderr);
252 			goto error;
253 		}
254 		else {
255 		}
256 	}
257 
258 #endif
259 
260 #ifdef ABORTIVE_CLOSE
261 #error do not enable this option
262     /* set up for abortive close */
263     linger_opt.l_onoff = 1;
264     linger_opt.l_linger = 0;
265     if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &linger_opt,
266 					sizeof(linger_opt)) < 0)
267 	{
268 		fprintf(stderr, "Can't set sockopt SO_LINGER");
269 		returnerr("Couldn't set SO_LINGER = 0\n");
270 		goto error;
271     }
272 #endif /* ABORTIVE_CLOSE */
273 
274     if(GETTIMEOFDAY(&timer->afterconnect, &timer->afterconnectzone) != 0)
275     {
276 #ifdef STONE_SSL
277 		if(do_ssl)
278 			SSL_shutdown(ssl);
279 #endif
280 
281 		NETCLOSE(sock);
282 		GETTIMEOFDAY(&timer->exittime, &timer->exittimezone);
283 		returnerr("Error retrieving afterconnect\n");
284 		goto error;
285     }
286 
287     /*
288      * SEND THE GET AND THE ACCEPT.
289      */
290     sprintf(getcommand, "GET %s HTTP/1.0\r\n%s", url, ACCEPT_COMMAND);
291     D_PRINTF( "Writing to server: %s\n", getcommand );
292     writelen = strlen(getcommand);
293 
294 #ifdef STONE_SSL
295 	if(do_ssl)
296 		status = SSL_write(ssl, getcommand, writelen);
297 	else
298 #endif
299     	status = NETWRITE(sock, getcommand, writelen);
300 
301     if(status != writelen)
302     {
303 		returnerr("Error sending command line to server: %s\n", neterrstr());
304 		goto error;
305     }
306     /*
307      * WE HAVE NOW SENT THE REQUEST SUCCESSFULLY.
308      * WAIT FOR THE REPLY AND FIND THE HEADER
309      */
310 
311     if(GETTIMEOFDAY(&timer->beforeheader, &timer->beforeheaderzone) != 0)
312     {
313 		returnerr("Error retrieving beforeheader\n");
314 		goto error;
315     }
316 
317     /* read the header and part of the file */
318     totalbytesread = 0;
319     headerlen = 0;
320     while (totalbytesread < HEADERBUFSIZ)
321     {
322         if (timeexpired)
323 			goto error;	/* this won't really be counted as an error */
324 
325 		TRACE("get(): about to read header\n");
326 
327 #ifdef STONE_SSL
328 		if(do_ssl)
329 			bytesread = SSL_read(ssl, headerbuffer+totalbytesread,
330 							HEADERBUFSIZ-totalbytesread);
331 		else
332 #endif
333         	bytesread = NETREAD(sock, headerbuffer+totalbytesread,
334 							HEADERBUFSIZ-totalbytesread);
335 
336         if (timeexpired)
337 			goto error;	/* this won't really be counted as an error */
338         if (bytesread < 0)
339         {
340 			D_PRINTF( "Did not receive full header\n" );
341 			D_PRINTF( "NETREAD returned %d\n", bytesread );
342 			returnerr("Did not receive full header: %s\n", neterrstr());
343 			goto error;
344         }
345         totalbytesread += bytesread;
346 
347 		/* search for end of header */
348 		headerbuffer[totalbytesread] = 0;
349 		if (offset = strstr(headerbuffer, "\n\n"))
350 		{
351 			headerlen = offset - headerbuffer + 2;
352 			break;
353 		}
354 		else if (offset = strstr(headerbuffer, "\n\r\n"))
355 		{
356 			headerlen = offset - headerbuffer + 3;
357 			break;
358 		}
359     }
360 
361     if (headerlen == 0)
362 	{
363 		returnerr("Can't find the end of the header in \"%s\"\n", headerbuffer);
364 		goto error;
365     }
366 
367     /* get and check status code from the first line of the header */
368     count = sscanf(headerbuffer, "HTTP/%s %d", version, &status);
369     if (count != 2)
370 	{
371 		returnerr("Bad status line in get(): %s\n", headerbuffer);
372 		goto error;
373     }
374     if (status < 200 || status > 300)
375 	{
376 		returnerr("Bad status (%d) in get() for url %s\n", status, url);
377 		goto error;
378     }
379 
380     /* get the content length line from the header */
381     offset = headerbuffer;
382     while (offset < headerbuffer+headerlen && *offset)
383 	{
384 		if (*offset++ != '\n')
385 			continue;
386 
387 		if (strncasecmp(offset, CONTENT_LENGTH_STRING,
388 						strlen(CONTENT_LENGTH_STRING)) == 0)
389 		{
390 			sscanf(offset+strlen(CONTENT_LENGTH_STRING), "%d", &contentlength);
391 			D_PRINTF( "Content-Length: %d\n", contentlength );
392 		}
393     }
394 
395     if(GETTIMEOFDAY(&timer->afterheader, &timer->afterheaderzone) != 0)
396     {
397 		returnerr("Error retrieving afterheader\n");
398 		goto error;
399     }
400 
401     if(savefile)
402     {
403         sprintf(outputfilename,"%s%d", SAVE_FILE, (int)getpid());
404         if((outputfile = fopen(outputfilename, "w")) == (FILE *)NULL)
405         {
406             D_PRINTF( "outputfile %d %d\n", outputfile, errno );
407 			returnerr("Error saving file: %s\n", strerror(errno));
408 			goto error;
409         }
410 		/* lseek(outputfile,1,SEEK_END); */	/* this is odd... JEF */
411 
412 		/* if we have part of the file already, save that part */
413 		if(totalbytesread > headerlen)
414 			fwrite(headerbuffer+headerlen, 1, totalbytesread-headerlen,
415 				outputfile);
416     }
417 
418     /* read the body of the file */
419     do
420     {
421 		TRACE("get(): about to read body\n");
422 
423 #ifdef STONE_SSL
424 		if(do_ssl)
425 			bytesread = SSL_read(ssl, headerbuffer, HEADERBUFSIZ);
426 		else
427 #endif
428 			bytesread = NETREAD(sock, headerbuffer, HEADERBUFSIZ);
429 
430         if (timeexpired)
431 			goto error;	/* this won't really be counted as an error */
432 
433 		if (bytesread < 0)
434 		{
435 #ifdef STONE_SSL
436 			if(do_ssl)
437 				ERR_print_errors_fp(stderr);
438 #endif
439 			D_PRINTF( "Read returns %d, error: %s\n", bytesread, neterrstr() );
440 			returnerr("Error during read of page body. Read "
441 					 "returns %d on socket %d, error: %s\n",
442 					 bytesread, sock, neterrstr());
443 			goto error;
444 		}
445 
446 		totalbytesread += bytesread;
447 
448 		if (outputfile != (FILE *)NULL && bytesread)
449 			fwrite(headerbuffer, 1, bytesread, outputfile);
450     } while (bytesread && !timeexpired);
451 
452     /* done reading body */
453     if ( contentlength && (totalbytesread - headerlen) != contentlength)
454     {
455 		D_PRINTF( "Warning: file length (%d) doesn't match Content-length (%d)\n",
456 	    totalbytesread - headerlen, contentlength);
457     }
458 
459     bodylength = totalbytesread - headerlen;
460 
461     if(GETTIMEOFDAY(&timer->afterbody, &timer->afterbodyzone) != 0)
462     {
463 		returnerr("Error retrieving afterbody\n");
464 		goto error;
465     }
466 
467 #ifdef STONE_SSL
468 	if(do_ssl)
469 		SSL_shutdown(ssl);
470 #endif
471 
472     NETCLOSE(sock);
473 
474     if (outputfile != (FILE *)NULL)
475         fclose(outputfile);
476 
477     D_PRINTF( "Read %d bytes, %d of that being body\n",
478 				totalbytesread, bodylength );
479 
480     if(GETTIMEOFDAY(&timer->exittime, &timer->exittimezone) != 0)
481     {
482 		D_PRINTF( "Error retrieving exit time: %s\n", strerror(errno) );
483 		returnerr("Error retrieving exit time\n");
484 		goto error;
485     }
486     timer->valid = 2;
487     timer->totalbytes = totalbytesread;
488     timer->bodybytes = bodylength;
489 
490     D_PRINTF( "get returning totalbytes %d body %d valid %d\n",
491 	timer->totalbytes, timer->bodybytes, timer->valid );
492 
493     D_PRINTF( "get returning start %d, end %d\n",
494 	timer->entertime.tv_sec, timer->exittime.tv_sec	);
495 
496     D_PRINTF( "get returning connect %d, request %d, header %d, body %d\n",
497 	timer->afterconnect.tv_sec, timer->beforeheader.tv_sec,
498 	timer->afterheader.tv_sec, timer->afterbody.tv_sec );
499 
500 	TRACE("get(): exiting successfully\n");
501     return 0;
502 
503 error:
504 	TRACE("get(): entering error section\n");
505 #ifdef STONE_SSL
506 	if(do_ssl)
507         SSL_shutdown(ssl);
508 #endif
509     if (!BADSOCKET(sock)) {
510         NETCLOSE(sock);
511     }
512 
513     if (outputfile != (FILE *)NULL)
514 		fclose(outputfile);
515     GETTIMEOFDAY(&timer->exittime, &timer->exittimezone);
516 		/* needed for record_all_transactions option in webclient.c */
517     return -1;
518 }
519