1 /* http_ping - measure HTTP latency
2 **
3 ** Copyright � 1998,1999,2001,2002 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 **    notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 **    notice, this list of conditions and the following disclaimer in the
13 **    documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27 
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netdb.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <setjmp.h>
41 
42 #ifdef USE_SSL
43 #include <openssl/ssl.h>
44 #include <openssl/rand.h>
45 #include <openssl/err.h>
46 #endif
47 
48 #include "port.h"
49 
50 #define INTERVAL 5
51 #define TIMEOUT 15
52 
53 #define max(a,b) ((a)>=(b)?(a):(b))
54 #define min(a,b) ((a)<=(b)?(a):(b))
55 
56 static char* url;
57 static int url_protocol;
58 static char url_host[5000];
59 static unsigned short url_port;
60 static char* url_filename;
61 
62 /* Protocol symbols. */
63 #define PROTO_HTTP 0
64 #ifdef USE_SSL
65 #define PROTO_HTTPS 1
66 #endif
67 
68 static unsigned short port;
69 static int conn_fd;
70 #ifdef USE_SSL
71 static SSL* ssl;
72 #endif
73 static int conn_state, conn_state;
74 static int got_response;
75 static struct timeval started_at, connect_at, response_at, finished_at;
76 static long content_length;
77 static long bytes;
78 
79 #define ST_BOL 0
80 #define ST_TEXT 1
81 #define ST_LF 2
82 #define ST_CR 3
83 #define ST_CRLF 4
84 #define ST_CRLFCR 5
85 #define ST_C 6
86 #define ST_CO 7
87 #define ST_CON 8
88 #define ST_CONT 9
89 #define ST_CONTE 10
90 #define ST_CONTEN 11
91 #define ST_CONTENT 12
92 #define ST_CONTENT_ 13
93 #define ST_CONTENT_L 14
94 #define ST_CONTENT_LE 15
95 #define ST_CONTENT_LEN 16
96 #define ST_CONTENT_LENG 17
97 #define ST_CONTENT_LENGT 18
98 #define ST_CONTENT_LENGTH 19
99 #define ST_CONTENT_LENGTH_COLON 20
100 #define ST_CONTENT_LENGTH_COLON_WHITESPACE 21
101 #define ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM 22
102 #define ST_DATA 23
103 
104 static char* argv0;
105 static int count;
106 static int interval;
107 static int quiet;
108 static int do_proxy;
109 static char* proxy_host;
110 static unsigned short proxy_port;
111 
112 static int terminate;
113 static int count_started, count_completed, count_failures, count_timeouts;
114 static long total_bytes;
115 static jmp_buf jb;
116 
117 static float min_total, min_connect, min_response, min_data;
118 static float max_total, max_connect, max_response, max_data;
119 static float sum_total, sum_connect, sum_response, sum_data;
120 
121 #ifdef USE_SSL
122 static SSL_CTX* ssl_ctx = (SSL_CTX*) 0;
123 #endif
124 
125 
126 /* Forwards. */
127 static void usage( void );
128 static void parse_url( void );
129 static void init_net( void );
130 static int start_connection( void );
131 static void lookup_address( char* hostname, unsigned short port );
132 static int open_client_socket( void );
133 static int handle_read( void );
134 static void handle_term( int sig );
135 static void handle_alarm( int sig );
136 static void close_connection( void );
137 static long long delta_timeval( struct timeval* start, struct timeval* finish );
138 
139 
140 int
main(int argc,char ** argv)141 main( int argc, char** argv )
142     {
143     int argn;
144     float elapsed_total, elapsed_connect, elapsed_response, elapsed_data;
145 
146     /* Parse args. */
147     argv0 = argv[0];
148     argn = 1;
149     count = -1;
150     interval = INTERVAL;
151     quiet = 0;
152     do_proxy = 0;
153     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
154 	{
155 	if ( strncmp( argv[argn], "-count", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
156 	    {
157 	    count = atoi( argv[++argn] );
158 	    if ( count <= 0 )
159 		{
160 		(void) fprintf( stderr, "%s: count must be positive\n", argv0 );
161 		exit( 1 );
162 		}
163 	    }
164 	else if ( strncmp( argv[argn], "-interval", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
165 	    {
166 	    interval = atoi( argv[++argn] );
167 	    if ( interval < 1 )
168 		{
169 		(void) fprintf( stderr, "%s: interval must be at least one\n", argv0 );
170 		exit( 1 );
171 		}
172 	    }
173 	else if ( strncmp( argv[argn], "-quiet", strlen( argv[argn] ) ) == 0 )
174 	    {
175 	    quiet = 1;
176 	    }
177 	else if ( strncmp( argv[argn], "-proxy", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
178 	    {
179 	    char* colon;
180 	    do_proxy = 1;
181 	    proxy_host = argv[++argn];
182 	    colon = strchr( proxy_host, ':' );
183 	    if ( colon == (char*) 0 )
184 		proxy_port = 80;
185 	    else
186 		{
187 		proxy_port = (unsigned short) atoi( colon + 1 );
188 		*colon = '\0';
189 		}
190 	    }
191 	else
192 	    usage();
193 	++argn;
194 	}
195     if ( argn + 1 != argc )
196 	usage();
197     url = argv[argn];
198 
199     /* Parse the URL. */
200     parse_url();
201 
202     /* Initialize the network stuff. */
203     init_net();
204 
205     /* Initialize the statistics. */
206     count_started = count_completed = count_failures = count_timeouts = 0;
207     total_bytes = 0;
208     min_total = min_connect = min_response = min_data = 1000000000.0;
209     max_total = max_connect = max_response = max_data = -1000000000.0;
210     sum_total = sum_connect = sum_response = sum_data = 0.0;
211 
212     /* Initialize the random number generator. */
213 #ifdef HAVE_SRANDOMDEV
214     srandomdev();
215 #else
216     srandom( (int) time( (time_t*) 0 ) ^ getpid() );
217 #endif
218 
219     /* Initialize the rest. */
220 #ifdef HAVE_SIGSET
221     (void) sigset( SIGTERM, handle_term );
222     (void) sigset( SIGINT, handle_term );
223     (void) sigset( SIGPIPE, SIG_IGN );
224     (void) sigset( SIGALRM, handle_alarm );
225 #else /* HAVE_SIGSET */
226     (void) signal( SIGTERM, handle_term );
227     (void) signal( SIGINT, handle_term );
228     (void) signal( SIGPIPE, SIG_IGN );
229     (void) signal( SIGALRM, handle_alarm );
230 #endif /* HAVE_SIGSET */
231 
232     /* Main loop. */
233     terminate = 0;
234     for (;;)
235 	{
236 	(void) setjmp( jb );
237 	if ( count == 0 || terminate )
238 	    break;
239 	if ( count > 0 )
240 	    --count;
241 	++count_started;
242 	alarm( TIMEOUT );
243 	if ( ! start_connection() )
244 	    ++count_failures;
245 	else
246 	    {
247 	    if ( ! handle_read() )
248 		++count_failures;
249 	    else
250 		{
251 		++count_completed;
252 		elapsed_total =
253 		    delta_timeval( &started_at, &finished_at ) / 1000.0;
254 		elapsed_connect =
255 		    delta_timeval( &started_at, &connect_at ) / 1000.0;
256 		elapsed_response =
257 		    delta_timeval( &connect_at, &response_at ) / 1000.0;
258 		elapsed_data =
259 		    delta_timeval( &response_at, &finished_at ) / 1000.0;
260 		if ( ! quiet )
261 		    (void) printf(
262 			"%ld bytes from %s: %g ms (%gc/%gr/%gd)\n",
263 			bytes, url, elapsed_total, elapsed_connect,
264 			elapsed_response, elapsed_data );
265 		min_total = min( min_total, elapsed_total );
266 		min_connect = min( min_connect, elapsed_connect );
267 		min_response = min( min_response, elapsed_response );
268 		min_data = min( min_data, elapsed_data );
269 		max_total = max( max_total, elapsed_total );
270 		max_connect = max( max_connect, elapsed_connect );
271 		max_response = max( max_response, elapsed_response );
272 		max_data = max( max_data, elapsed_data );
273 		sum_total += elapsed_total;
274 		sum_connect += elapsed_connect;
275 		sum_response += elapsed_response;
276 		sum_data += elapsed_data;
277 		}
278 	    }
279 	alarm( 0 );
280 	if ( count == 0 || terminate )
281 	    break;
282 	sleep( interval );
283 	}
284 
285     /* Report statistics. */
286     (void) printf( "\n" );
287     (void) printf( "--- %s http_ping statistics ---\n", url );
288     (void) printf(
289 	"%d fetches started, %d completed (%d%%), %d failures (%d%%), %d timeouts (%d%%)\n",
290 	count_started, count_completed, count_completed * 100 / count_started,
291 	count_failures, count_failures * 100 / count_started,
292 	count_timeouts, count_timeouts * 100 / count_started );
293     if ( count_completed > 0 )
294 	{
295 	(void) printf(
296 	    "total    min/avg/max = %g/%g/%g ms\n",
297 	    min_total, sum_total / count_completed, max_total );
298 	(void) printf(
299 	    "connect  min/avg/max = %g/%g/%g ms\n",
300 	    min_connect, sum_connect / count_completed, max_connect );
301 	(void) printf(
302 	    "response min/avg/max = %g/%g/%g ms\n",
303 	    min_response, sum_response / count_completed, max_response );
304 	(void) printf(
305 	    "data     min/avg/max = %g/%g/%g ms\n",
306 	    min_data, sum_data / count_completed, max_data );
307 	}
308 
309     /* Done. */
310 #ifdef USE_SSL
311     if ( ssl_ctx != (SSL_CTX*) 0 )
312 	SSL_CTX_free( ssl_ctx );
313 #endif
314     exit( 0 );
315     }
316 
317 
318 static void
usage(void)319 usage( void )
320     {
321     (void) fprintf( stderr, "usage:  %s [-count n] [-interval n] [-quiet] [-proxy host:port] url\n", argv0 );
322     exit( 1 );
323     }
324 
325 
326 static void
parse_url(void)327 parse_url( void )
328     {
329     char* http = "http://";
330     int http_len = strlen( http );
331 #ifdef USE_SSL
332     char* https = "https://";
333     int https_len = strlen( https );
334 #endif
335     int proto_len, host_len;
336     char* cp;
337 
338     if ( strncmp( http, url, http_len ) == 0 )
339 	{
340 	proto_len = http_len;
341 	url_protocol = PROTO_HTTP;
342 	}
343 #ifdef USE_SSL
344     else if ( strncmp( https, url, https_len ) == 0 )
345 	{
346 	proto_len = https_len;
347 	url_protocol = PROTO_HTTPS;
348 	}
349 #endif
350     else
351 	{
352 	(void) fprintf( stderr, "%s: unknown protocol - %s\n", argv0, url );
353 	exit( 1 );
354 	}
355     for ( cp = url + proto_len;
356 	 *cp != '\0' && *cp != ':' && *cp != '/'; ++cp )
357 	;
358     host_len = cp - url;
359     host_len -= proto_len;
360     host_len = min( host_len, sizeof(url_host) - 1 );
361     strncpy( url_host, url + proto_len, host_len );
362     url_host[host_len] = '\0';
363     if ( *cp == ':' )
364 	{
365 	url_port = (unsigned short) atoi( ++cp );
366 	while ( *cp != '\0' && *cp != '/' )
367 	    ++cp;
368 	}
369     else
370 #ifdef USE_SSL
371 	if ( url_protocol == PROTO_HTTPS )
372 	    url_port = 443;
373 	else
374 	    url_port = 80;
375 #else
376 	url_port = 80;
377 #endif
378     if ( *cp == '\0' )
379 	url_filename = "/";
380     else
381 	url_filename = cp;
382     }
383 
384 
385 static void
init_net(void)386 init_net( void )
387     {
388     char* host;
389 
390     if ( do_proxy )
391 	{
392 	host = proxy_host;
393 	port = proxy_port;
394 	}
395     else
396 	{
397 	host = url_host;
398 	port = url_port;
399 	}
400     lookup_address( host, port );
401     }
402 
403 
404 static int
start_connection(void)405 start_connection( void )
406     {
407     char buf[600];
408     int b, r;
409 
410     (void) gettimeofday( &started_at, (struct timezone*) 0 );
411     got_response = 0;
412     content_length = -1;
413     bytes = 0;
414 
415     conn_fd = open_client_socket();
416     if ( conn_fd < 0 )
417 	return 0;
418 
419 #ifdef USE_SSL
420     if ( url_protocol == PROTO_HTTPS )
421 	{
422 	/* Complete the SSL connection. */
423 	if ( ssl_ctx == (SSL_CTX*) 0 )
424 	    {
425 	    SSL_load_error_strings();
426 	    SSLeay_add_ssl_algorithms();
427 	    ssl_ctx = SSL_CTX_new( SSLv23_client_method() );
428 	    }
429 	if ( ! RAND_status() )
430 	    {
431 	    unsigned char rb[1024];
432 	    int i;
433 	    for ( i = 0; i < sizeof(rb); ++i )
434 		rb[i] = random() % 0xff;
435 	    RAND_seed( rb, sizeof(rb) );
436 	    }
437 	ssl = SSL_new( ssl_ctx );
438 	SSL_set_fd( ssl, conn_fd );
439 	r = SSL_connect( ssl );
440 	if ( r <= 0 )
441 	    {
442 	    (void) fprintf(
443 		stderr, "%s: SSL connection failed - %d\n", argv0, r );
444 	    ERR_print_errors_fp( stderr );
445 	    close_connection();
446 	    return 0;
447 	    }
448 	}
449 #endif
450     (void) gettimeofday( &connect_at, (struct timezone*) 0 );
451 
452     /* Format the request. */
453     if ( do_proxy )
454 	{
455 #ifdef USE_SSL
456 	b = snprintf(
457 	    buf, sizeof(buf), "GET %s://%.500s:%d%.500s HTTP/1.0\r\n",
458 	    url_protocol == PROTO_HTTPS ? "https" : "http", url_host,
459 	    (int) url_port, url_filename );
460 #else
461 	b = snprintf(
462 	    buf, sizeof(buf), "GET http://%.500s:%d%.500s HTTP/1.0\r\n",
463 	    url_host, (int) url_port, url_filename );
464 #endif
465 	}
466     else
467 	b = snprintf(
468 	    buf, sizeof(buf), "GET %.500s HTTP/1.0\r\n", url_filename );
469     b += snprintf( &buf[b], sizeof(buf) - b, "Host: %s\r\n", url_host );
470     b += snprintf( &buf[b], sizeof(buf) - b, "User-Agent: http_ping\r\n" );
471     b += snprintf( &buf[b], sizeof(buf) - b, "\r\n" );
472 
473     /* Send the request. */
474 #ifdef USE_SSL
475     if ( url_protocol == PROTO_HTTPS )
476 	r = SSL_write( ssl, buf, b );
477     else
478 	r = write( conn_fd, buf, b );
479 #else
480     r = write( conn_fd, buf, b );
481 #endif
482     if ( r < 0 )
483 	{
484 	perror( "write" );
485 	close_connection();
486 	return 0;
487 	}
488     conn_state = ST_BOL;
489     return 1;
490     }
491 
492 
493 #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
494 #define USE_IPV6
495 #endif
496 
497 #ifdef USE_IPV6
498 static struct sockaddr_in6 sa;
499 #else /* USE_IPV6 */
500 static struct sockaddr_in sa;
501 #endif /* USE_IPV6 */
502 static int sa_len, sock_family, sock_type, sock_protocol;
503 
504 
505 static void
lookup_address(char * hostname,unsigned short port)506 lookup_address( char* hostname, unsigned short port )
507     {
508 #ifdef USE_IPV6
509     struct addrinfo hints;
510     char portstr[10];
511     int gaierr;
512     struct addrinfo* ai;
513     struct addrinfo* ai2;
514     struct addrinfo* aiv4;
515     struct addrinfo* aiv6;
516 #else /* USE_IPV6 */
517     struct hostent *he;
518 #endif /* USE_IPV6 */
519 
520     (void) memset( (void*) &sa, 0, sizeof(sa) );
521 
522 #ifdef USE_IPV6
523 
524     (void) memset( &hints, 0, sizeof(hints) );
525     hints.ai_family = PF_UNSPEC;
526     hints.ai_socktype = SOCK_STREAM;
527     (void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
528     if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
529 	{
530 	(void) fprintf(
531 	    stderr, "%s: getaddrinfo %s - %s\n", argv0, hostname,
532 	    gai_strerror( gaierr ) );
533 	exit( 1 );
534 	}
535 
536     /* Find the first IPv4 and IPv6 entries. */
537     aiv4 = (struct addrinfo*) 0;
538     aiv6 = (struct addrinfo*) 0;
539     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
540 	{
541 	switch ( ai2->ai_family )
542 	    {
543 	    case AF_INET:
544 	    if ( aiv4 == (struct addrinfo*) 0 )
545 		aiv4 = ai2;
546 	    break;
547 	    case AF_INET6:
548 	    if ( aiv6 == (struct addrinfo*) 0 )
549 		aiv6 = ai2;
550 	    break;
551 	    }
552 	}
553 
554     /* If there's an IPv4 address, use that, otherwise try IPv6. */
555     if ( aiv4 != (struct addrinfo*) 0 )
556 	{
557 	if ( sizeof(sa) < aiv4->ai_addrlen )
558 	    {
559 	    (void) fprintf(
560 		stderr, "%s - sockaddr too small (%lu < %lu)\n",
561 		hostname, (unsigned long) sizeof(sa),
562 		(unsigned long) aiv4->ai_addrlen );
563 	    exit( 1 );
564 	    }
565 	sock_family = aiv4->ai_family;
566 	sock_type = aiv4->ai_socktype;
567 	sock_protocol = aiv4->ai_protocol;
568 	sa_len = aiv4->ai_addrlen;
569 	(void) memmove( &sa, aiv4->ai_addr, sa_len );
570 	freeaddrinfo( ai );
571 	return;
572 	}
573     if ( aiv6 != (struct addrinfo*) 0 )
574 	{
575 	if ( sizeof(sa) < aiv6->ai_addrlen )
576 	    {
577 	    (void) fprintf(
578 		stderr, "%s - sockaddr too small (%lu < %lu)\n",
579 		hostname, (unsigned long) sizeof(sa),
580 		(unsigned long) aiv6->ai_addrlen );
581 	    exit( 1 );
582 	    }
583 	sock_family = aiv6->ai_family;
584 	sock_type = aiv6->ai_socktype;
585 	sock_protocol = aiv6->ai_protocol;
586 	sa_len = aiv6->ai_addrlen;
587 	(void) memmove( &sa, aiv6->ai_addr, sa_len );
588 	freeaddrinfo( ai );
589 	return;
590 	}
591 
592     (void) fprintf(
593 	stderr, "%s: no valid address found for host %s\n", argv0, hostname );
594     exit( 1 );
595 
596 #else /* USE_IPV6 */
597 
598     he = gethostbyname( hostname );
599     if ( he == (struct hostent*) 0 )
600 	{
601 	(void) fprintf( stderr, "%s: unknown host - %s\n", argv0, hostname );
602 	exit( 1 );
603 	}
604     sock_family = sa.sin_family = he->h_addrtype;
605     sock_type = SOCK_STREAM;
606     sock_protocol = 0;
607     sa_len = sizeof(sa);
608     (void) memmove( &sa.sin_addr, he->h_addr, he->h_length );
609     sa.sin_port = htons( port );
610 
611 #endif /* USE_IPV6 */
612 
613     }
614 
615 
616 static int
open_client_socket(void)617 open_client_socket( void )
618     {
619     int sockfd;
620 
621     sockfd = socket( sock_family, sock_type, sock_protocol );
622     if ( sockfd < 0 )
623 	{
624 	perror( "socket" );
625 	return -1;
626 	}
627 
628     if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 )
629 	{
630 	perror( "connect" );
631 	(void) close( sockfd );
632 	return -1;
633 	}
634 
635     return sockfd;
636     }
637 
638 
639 static int
handle_read(void)640 handle_read( void )
641     {
642     char buf[5000];
643     int bytes_to_read, bytes_read, bytes_handled;
644 
645     for (;;)
646 	{
647 	bytes_to_read = sizeof(buf);
648 #ifdef USE_SSL
649 	if ( url_protocol == PROTO_HTTPS )
650 	    bytes_read = SSL_read( ssl, buf, bytes_to_read );
651 	else
652 	    bytes_read = read( conn_fd, buf, bytes_to_read );
653 #else
654 	bytes_read = read( conn_fd, buf, bytes_to_read );
655 #endif
656 	if ( bytes_read < 0 )
657 	    {
658 	    perror( "read" );
659 	    close_connection();
660 	    return 0;
661 	    }
662 	if ( ! got_response )
663 	    {
664 	    got_response = 1;
665 	    (void) gettimeofday( &response_at, (struct timezone*) 0 );
666 	    }
667 	if ( bytes_read == 0 )
668 	    {
669 	    close_connection();
670 	    (void) gettimeofday( &finished_at, (struct timezone*) 0 );
671 	    return 1;
672 	    }
673 
674 	for ( bytes_handled = 0; bytes_handled < bytes_read; ++bytes_handled )
675 	    {
676 	    switch ( conn_state )
677 		{
678 		case ST_BOL:
679 		switch ( buf[bytes_handled] )
680 		    {
681 		    case '\n':
682 		    conn_state = ST_LF;
683 		    break;
684 		    case '\r':
685 		    conn_state = ST_CR;
686 		    break;
687 		    case 'C': case 'c':
688 		    conn_state = ST_C;
689 		    break;
690 		    default:
691 		    conn_state = ST_TEXT;
692 		    break;
693 		    }
694 		break;
695 
696 		case ST_TEXT:
697 		switch ( buf[bytes_handled] )
698 		    {
699 		    case '\n':
700 		    conn_state = ST_LF;
701 		    break;
702 		    case '\r':
703 		    conn_state = ST_CR;
704 		    break;
705 		    default:
706 		    break;
707 		    }
708 		break;
709 
710 		case ST_LF:
711 		switch ( buf[bytes_handled] )
712 		    {
713 		    case '\n':
714 		    conn_state = ST_DATA;
715 		    break;
716 		    case '\r':
717 		    conn_state = ST_CR;
718 		    break;
719 		    case 'C': case 'c':
720 		    conn_state = ST_C;
721 		    break;
722 		    default:
723 		    conn_state = ST_TEXT;
724 		    break;
725 		    }
726 		break;
727 
728 		case ST_CR:
729 		switch ( buf[bytes_handled] )
730 		    {
731 		    case '\n':
732 		    conn_state = ST_CRLF;
733 		    break;
734 		    case '\r':
735 		    conn_state = ST_DATA;
736 		    break;
737 		    case 'C': case 'c':
738 		    conn_state = ST_C;
739 		    break;
740 		    default:
741 		    conn_state = ST_TEXT;
742 		    break;
743 		    }
744 		break;
745 
746 		case ST_CRLF:
747 		switch ( buf[bytes_handled] )
748 		    {
749 		    case '\n':
750 		    conn_state = ST_DATA;
751 		    break;
752 		    case '\r':
753 		    conn_state = ST_CRLFCR;
754 		    break;
755 		    case 'C': case 'c':
756 		    conn_state = ST_C;
757 		    break;
758 		    default:
759 		    conn_state = ST_TEXT;
760 		    break;
761 		    }
762 		break;
763 
764 		case ST_CRLFCR:
765 		switch ( buf[bytes_handled] )
766 		    {
767 		    case '\n': case '\r':
768 		    conn_state = ST_DATA;
769 		    break;
770 		    case 'C': case 'c':
771 		    conn_state = ST_C;
772 		    break;
773 		    default:
774 		    conn_state = ST_TEXT;
775 		    break;
776 		    }
777 		break;
778 
779 		case ST_C:
780 		switch ( buf[bytes_handled] )
781 		    {
782 		    case 'O': case 'o':
783 		    conn_state = ST_CO;
784 		    break;
785 		    case '\n':
786 		    conn_state = ST_LF;
787 		    break;
788 		    case '\r':
789 		    conn_state = ST_CR;
790 		    break;
791 		    default:
792 		    conn_state = ST_TEXT;
793 		    break;
794 		    }
795 		break;
796 
797 		case ST_CO:
798 		switch ( buf[bytes_handled] )
799 		    {
800 		    case 'N': case 'n':
801 		    conn_state = ST_CON;
802 		    break;
803 		    case '\n':
804 		    conn_state = ST_LF;
805 		    break;
806 		    case '\r':
807 		    conn_state = ST_CR;
808 		    break;
809 		    default:
810 		    conn_state = ST_TEXT;
811 		    break;
812 		    }
813 		break;
814 
815 		case ST_CON:
816 		switch ( buf[bytes_handled] )
817 		    {
818 		    case 'T': case 't':
819 		    conn_state = ST_CONT;
820 		    break;
821 		    case '\n':
822 		    conn_state = ST_LF;
823 		    break;
824 		    case '\r':
825 		    conn_state = ST_CR;
826 		    break;
827 		    default:
828 		    conn_state = ST_TEXT;
829 		    break;
830 		    }
831 		break;
832 
833 		case ST_CONT:
834 		switch ( buf[bytes_handled] )
835 		    {
836 		    case 'E': case 'e':
837 		    conn_state = ST_CONTE;
838 		    break;
839 		    case '\n':
840 		    conn_state = ST_LF;
841 		    break;
842 		    case '\r':
843 		    conn_state = ST_CR;
844 		    break;
845 		    default:
846 		    conn_state = ST_TEXT;
847 		    break;
848 		    }
849 		break;
850 
851 		case ST_CONTE:
852 		switch ( buf[bytes_handled] )
853 		    {
854 		    case 'N': case 'n':
855 		    conn_state = ST_CONTEN;
856 		    break;
857 		    case '\n':
858 		    conn_state = ST_LF;
859 		    break;
860 		    case '\r':
861 		    conn_state = ST_CR;
862 		    break;
863 		    default:
864 		    conn_state = ST_TEXT;
865 		    break;
866 		    }
867 		break;
868 
869 		case ST_CONTEN:
870 		switch ( buf[bytes_handled] )
871 		    {
872 		    case 'T': case 't':
873 		    conn_state = ST_CONTENT;
874 		    break;
875 		    case '\n':
876 		    conn_state = ST_LF;
877 		    break;
878 		    case '\r':
879 		    conn_state = ST_CR;
880 		    break;
881 		    default:
882 		    conn_state = ST_TEXT;
883 		    break;
884 		    }
885 		break;
886 
887 		case ST_CONTENT:
888 		switch ( buf[bytes_handled] )
889 		    {
890 		    case '-':
891 		    conn_state = ST_CONTENT_;
892 		    break;
893 		    case '\n':
894 		    conn_state = ST_LF;
895 		    break;
896 		    case '\r':
897 		    conn_state = ST_CR;
898 		    break;
899 		    default:
900 		    conn_state = ST_TEXT;
901 		    break;
902 		    }
903 		break;
904 
905 		case ST_CONTENT_:
906 		switch ( buf[bytes_handled] )
907 		    {
908 		    case 'L': case 'l':
909 		    conn_state = ST_CONTENT_L;
910 		    break;
911 		    case '\n':
912 		    conn_state = ST_LF;
913 		    break;
914 		    case '\r':
915 		    conn_state = ST_CR;
916 		    break;
917 		    default:
918 		    conn_state = ST_TEXT;
919 		    break;
920 		    }
921 		break;
922 
923 		case ST_CONTENT_L:
924 		switch ( buf[bytes_handled] )
925 		    {
926 		    case 'E': case 'e':
927 		    conn_state = ST_CONTENT_LE;
928 		    break;
929 		    case '\n':
930 		    conn_state = ST_LF;
931 		    break;
932 		    case '\r':
933 		    conn_state = ST_CR;
934 		    break;
935 		    default:
936 		    conn_state = ST_TEXT;
937 		    break;
938 		    }
939 		break;
940 
941 		case ST_CONTENT_LE:
942 		switch ( buf[bytes_handled] )
943 		    {
944 		    case 'N': case 'n':
945 		    conn_state = ST_CONTENT_LEN;
946 		    break;
947 		    case '\n':
948 		    conn_state = ST_LF;
949 		    break;
950 		    case '\r':
951 		    conn_state = ST_CR;
952 		    break;
953 		    default:
954 		    conn_state = ST_TEXT;
955 		    break;
956 		    }
957 		break;
958 
959 		case ST_CONTENT_LEN:
960 		switch ( buf[bytes_handled] )
961 		    {
962 		    case 'G': case 'g':
963 		    conn_state = ST_CONTENT_LENG;
964 		    break;
965 		    case '\n':
966 		    conn_state = ST_LF;
967 		    break;
968 		    case '\r':
969 		    conn_state = ST_CR;
970 		    break;
971 		    default:
972 		    conn_state = ST_TEXT;
973 		    break;
974 		    }
975 		break;
976 
977 		case ST_CONTENT_LENG:
978 		switch ( buf[bytes_handled] )
979 		    {
980 		    case 'T': case 't':
981 		    conn_state = ST_CONTENT_LENGT;
982 		    break;
983 		    case '\n':
984 		    conn_state = ST_LF;
985 		    break;
986 		    case '\r':
987 		    conn_state = ST_CR;
988 		    break;
989 		    default:
990 		    conn_state = ST_TEXT;
991 		    break;
992 		    }
993 		break;
994 
995 		case ST_CONTENT_LENGT:
996 		switch ( buf[bytes_handled] )
997 		    {
998 		    case 'H': case 'h':
999 		    conn_state = ST_CONTENT_LENGTH;
1000 		    break;
1001 		    case '\n':
1002 		    conn_state = ST_LF;
1003 		    break;
1004 		    case '\r':
1005 		    conn_state = ST_CR;
1006 		    break;
1007 		    default:
1008 		    conn_state = ST_TEXT;
1009 		    break;
1010 		    }
1011 		break;
1012 
1013 		case ST_CONTENT_LENGTH:
1014 		switch ( buf[bytes_handled] )
1015 		    {
1016 		    case ':':
1017 		    conn_state = ST_CONTENT_LENGTH_COLON;
1018 		    break;
1019 		    case '\n':
1020 		    conn_state = ST_LF;
1021 		    break;
1022 		    case '\r':
1023 		    conn_state = ST_CR;
1024 		    break;
1025 		    default:
1026 		    conn_state = ST_TEXT;
1027 		    break;
1028 		    }
1029 		break;
1030 
1031 		case ST_CONTENT_LENGTH_COLON:
1032 		switch ( buf[bytes_handled] )
1033 		    {
1034 		    case ' ': case '\t':
1035 		    conn_state = ST_CONTENT_LENGTH_COLON_WHITESPACE;
1036 		    break;
1037 		    case '\n':
1038 		    conn_state = ST_LF;
1039 		    break;
1040 		    case '\r':
1041 		    conn_state = ST_CR;
1042 		    break;
1043 		    default:
1044 		    conn_state = ST_TEXT;
1045 		    break;
1046 		    }
1047 		break;
1048 
1049 		case ST_CONTENT_LENGTH_COLON_WHITESPACE:
1050 		switch ( buf[bytes_handled] )
1051 		    {
1052 		    case ' ': case '\t':
1053 		    break;
1054 		    case '0': case '1': case '2': case '3': case '4':
1055 		    case '5': case '6': case '7': case '8': case '9':
1056 		    content_length = buf[bytes_handled] - '0';
1057 		    conn_state = ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM;
1058 		    break;
1059 		    case '\n':
1060 		    conn_state = ST_LF;
1061 		    break;
1062 		    case '\r':
1063 		    conn_state = ST_CR;
1064 		    break;
1065 		    default:
1066 		    conn_state = ST_TEXT;
1067 		    break;
1068 		    }
1069 		break;
1070 
1071 		case ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM:
1072 		switch ( buf[bytes_handled] )
1073 		    {
1074 		    case '0': case '1': case '2': case '3': case '4':
1075 		    case '5': case '6': case '7': case '8': case '9':
1076 		    content_length =
1077 			content_length * 10 + buf[bytes_handled] - '0';
1078 		    break;
1079 		    case '\n':
1080 		    conn_state = ST_LF;
1081 		    break;
1082 		    case '\r':
1083 		    conn_state = ST_CR;
1084 		    break;
1085 		    default:
1086 		    conn_state = ST_TEXT;
1087 		    break;
1088 		    }
1089 		break;
1090 
1091 		case ST_DATA:
1092 		bytes += bytes_read - bytes_handled;
1093 		total_bytes += bytes_read - bytes_handled;
1094 		bytes_handled = bytes_read;
1095 		if ( content_length != -1 && bytes >= content_length )
1096 		    {
1097 		    close_connection();
1098 		    (void) gettimeofday( &finished_at, (struct timezone*) 0 );
1099 		    return 1;
1100 		    }
1101 		break;
1102 		}
1103 	    }
1104 	}
1105     }
1106 
1107 
1108 static void
handle_term(int sig)1109 handle_term( int sig )
1110     {
1111     terminate = 1;
1112     }
1113 
1114 
1115 static void
handle_alarm(int sig)1116 handle_alarm( int sig )
1117     {
1118     close_connection();
1119     (void) fprintf( stderr, "%s: timed out\n", url );
1120     ++count_timeouts;
1121     longjmp( jb, 0 );
1122     }
1123 
1124 
1125 static void
close_connection(void)1126 close_connection( void )
1127     {
1128 #ifdef USE_SSL
1129     if ( url_protocol == PROTO_HTTPS )
1130 	SSL_free( ssl );
1131 #endif
1132     (void) close( conn_fd );
1133     }
1134 
1135 
1136 static long long
delta_timeval(struct timeval * start,struct timeval * finish)1137 delta_timeval( struct timeval* start, struct timeval* finish )
1138     {
1139     long long delta_secs = finish->tv_sec - start->tv_sec;
1140     long long delta_usecs = finish->tv_usec - start->tv_usec;
1141     return delta_secs * (long long) 1000000L + delta_usecs;
1142     }
1143