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