1 /**
2 * Browser instance
3 *
4 * Copyright (C) 2016 by
5 * Jeffrey Fulmer - <jeff@joedog.org>, et al.
6 * This file is distributed as part of Siege
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *--
22 */
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif/*HAVE_CONFIG_H*/
26
27 #include <setup.h>
28 #include <signal.h>
29 #include <sock.h>
30 #include <ssl.h>
31 #include <ftp.h>
32 #include <http.h>
33 #include <hash.h>
34 #include <array.h>
35 #include <util.h>
36 #include <parser.h>
37 #include <perl.h>
38 #include <response.h>
39 #include <memory.h>
40 #include <notify.h>
41 #include <browser.h>
42
43 #if defined(hpux) || defined(__hpux) || defined(WINDOWS)
44 # define SIGNAL_CLIENT_PLATFORM
45 #endif
46
47 #ifdef SIGNAL_CLIENT_PLATFORM
48 static pthread_once_t once = PTHREAD_ONCE_INIT;
49 #endif/*SIGNAL_CLIENT_PLATFORM*/
50
51 float __himark = 0;
52 float __lomark = -1;
53
54 struct BROWSER_T
55 {
56 int id;
57 size_t tid;
58 ARRAY urls;
59 ARRAY parts;
60 HASH cookies;
61 CONN * conn;
62 #ifdef SIGNAL_CLIENT_PLATFORM
63 sigset_t sigs;
64 #else
65 int type;
66 int state;
67 #endif
68 float total;
69 float available;
70 float lowest;
71 float highest;
72 float elapsed;
73 float time;
74 float himark;
75 float lomark;
76 clock_t start;
77 clock_t stop;
78 struct tms t_start;
79 struct tms t_stop;
80 struct {
81 DCHLG *wchlg;
82 DCRED *wcred;
83 int www;
84 DCHLG *pchlg;
85 DCRED *pcred;
86 int proxy;
87 struct {
88 int www;
89 int proxy;
90 } bids;
91 struct {
92 TYPE www;
93 TYPE proxy;
94 } type;
95 } auth;
96 unsigned int code;
97 unsigned int count;
98 unsigned int okay;
99 unsigned int fail;
100 unsigned long hits;
101 unsigned long long bytes;
102 unsigned int rseed;
103 };
104
105 size_t BROWSERSIZE = sizeof(struct BROWSER_T);
106
107 private BOOLEAN __init_connection(BROWSER this, URL U);
108 private BOOLEAN __request(BROWSER this, URL U);
109 private BOOLEAN __http(BROWSER this, URL U);
110 private BOOLEAN __ftp(BROWSER this, URL U);
111 private BOOLEAN __no_follow(const char *hostname);
112 private void __increment_failures();
113 private int __select_color(int code);
114 private void __display_result(BROWSER this, RESPONSE resp, URL U, unsigned long bytes, float etime);
115
116
117 #ifdef SIGNAL_CLIENT_PLATFORM
118 private void __signal_handler(int sig);
119 private void __signal_init();
120 #else/*CANCEL_CLIENT_PLATFORM*/
121 private void __signal_cleanup();
122 #endif/*SIGNAL_CLIENT_PLATFORM*/
123
124
125 BROWSER
new_browser(int id)126 new_browser(int id)
127 {
128 BROWSER this;
129
130 this = calloc(BROWSERSIZE,1);
131 this->id = id;
132 this->total = 0.0;
133 this->available = 0.0;
134 this->count = 0.0;
135 this->okay = 0;
136 this->fail = 0;
137 this->lowest = -1;
138 this->highest = 0.0;
139 this->elapsed = 0.0;
140 this->bytes = 0.0;
141 this->urls = NULL;
142 this->parts = new_array();
143 this->rseed = urandom();
144 return this;
145 }
146
147 BROWSER
browser_destroy(BROWSER this)148 browser_destroy(BROWSER this)
149 {
150 if (this != NULL) {
151 /**
152 * NOTE: this->urls is a reference to main.c:urls It was
153 * never instantiated in this class. We'll reclaim that
154 * memory when we deconstruct main.c:urls
155 */
156
157 if (this->parts != NULL) {
158 URL u;
159 while ((u = (URL)array_pop(this->parts)) != NULL) {
160 u = url_destroy(u);
161 }
162 this->parts = array_destroy(this->parts);
163 }
164 xfree(this);
165 }
166 this = NULL;
167 return this;
168 }
169
170 unsigned long
browser_get_hits(BROWSER this)171 browser_get_hits(BROWSER this)
172 {
173 return this->hits;
174 }
175
176 unsigned long long
browser_get_bytes(BROWSER this)177 browser_get_bytes(BROWSER this)
178 {
179 return this->bytes;
180 }
181
182 float
browser_get_time(BROWSER this)183 browser_get_time(BROWSER this)
184 {
185 return this->time;
186 }
187
188 unsigned int
browser_get_code(BROWSER this)189 browser_get_code(BROWSER this)
190 {
191 return this->code;
192 }
193
194 unsigned int
browser_get_okay(BROWSER this)195 browser_get_okay(BROWSER this)
196 {
197 return this->okay;
198 }
199
200 unsigned int
browser_get_fail(BROWSER this)201 browser_get_fail(BROWSER this)
202 {
203 return this->fail;
204 }
205
206 float
browser_get_himark(BROWSER this)207 browser_get_himark(BROWSER this)
208 {
209 return this->himark;
210 }
211
212 float
browser_get_lomark(BROWSER this)213 browser_get_lomark(BROWSER this)
214 {
215 return this->lomark;
216 }
217
218 void *
start(BROWSER this)219 start(BROWSER this)
220 {
221 int x;
222 int y;
223 int max_y;
224 int ret;
225 int len;
226 this->conn = NULL;
227 this->conn = xcalloc(sizeof(CONN), 1);
228 this->conn->sock = -1;
229 this->conn->page = new_page("");
230 this->conn->cache = new_cache();
231
232 #ifdef SIGNAL_CLIENT_PLATFORM
233 pthread_once(&this->once, __signal_init);
234 sigemptyset(&this->sigs);
235 sigaddset(&this->sigs, SIGUSR1);
236 pthread_sigmask(SIG_UNBLOCK, &this->sigs, NULL);
237 #else/*CANCEL_CLIENT_PLATFORM*/
238 #if defined(_AIX)
239 pthread_cleanup_push((void(*)(void*))__signal_cleanup, NULL);
240 #else
241 pthread_cleanup_push((void*)__signal_cleanup, this->conn);
242 #endif
243
244 #if defined(sun)
245 pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &this->type);
246 #elif defined(_AIX)
247 pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &this->type);
248 #elif defined(hpux) || defined(__hpux)
249 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &this->type);
250 #else
251 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &this->type);
252 #endif
253 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &this->state);
254 #endif/*SIGNAL_CLIENT_PLATFORM*/
255
256 if (my.login == TRUE) {
257 URL tmp = new_url(array_next(my.lurl));
258 url_set_ID(tmp, 0);
259 __request(this, tmp);
260 }
261
262 len = (my.reps == -1) ? (int)array_length(this->urls) : my.reps;
263 y = (my.reps == -1) ? 0 : this->id * (my.length / my.cusers);
264 max_y = (int)array_length(this->urls);
265 for (x = 0; x < len; x++, y++) {
266 x = ((my.secs > 0) && ((my.reps <= 0)||(my.reps == MAXREPS))) ? 0 : x;
267 if (my.internet == TRUE) {
268 y = (unsigned int) (((double)pthread_rand_np(&(this->rseed)) /
269 ((double)RAND_MAX + 1) * my.length ) + .5);
270 y = (y >= my.length)?my.length-1:y;
271 y = (y < 0)?0:y;
272 } else {
273 /**
274 * URLs accessed sequentially; when reaching the end, start over
275 * with clean slate, ie. reset (delete) cookies (eg. to let a new
276 * session start)
277 */
278 if (y >= max_y) {
279 y = 0;
280 if (my.expire) {
281 cookies_delete_all(my.cookies);
282 }
283 }
284 }
285 if (y >= max_y || y < 0) {
286 y = 0;
287 }
288
289 /**
290 * This is the initial request from the command line
291 * or urls.txt file. If it is text/html then it will
292 * be parsed in __http request function.
293 */
294 URL tmp = array_get(this->urls, y);
295 if (tmp != NULL && url_get_hostname(tmp) != NULL) {
296 this->auth.bids.www = 0; /* reset */
297 if ((ret = __request(this, tmp))==FALSE) {
298 __increment_failures();
299 }
300 }
301
302 /**
303 * If we parsed http resources, we'll request them here
304 */
305 if (my.parser == TRUE && this->parts != NULL) {
306 URL u;
307 while ((u = (URL)array_pop(this->parts)) != NULL) {
308 if (url_get_scheme(u) == UNSUPPORTED) {
309 ;;
310 } else if (my.cache && is_cached(this->conn->cache, u)) {
311 RESPONSE r = new_response();
312 response_set_code(r, "HTTP/1.1 200 OK");
313 response_set_from_cache(r, TRUE);
314 __display_result(this, r, u, 0, 0.00);
315 r = response_destroy(r);
316 ;;
317 } else {
318 this->auth.bids.www = 0;
319 // We'll only request files on the same host as the page
320 if (! __no_follow(url_get_hostname(u))) {
321 if ((ret = __request(this, u))==FALSE) {
322 __increment_failures();
323 }
324 }
325 }
326 u = url_destroy(u);
327 }
328 }
329
330 /**
331 * Delay between interactions -D num /--delay=num
332 */
333 if (my.delay >= 1) {
334 pthread_sleep_np(
335 (unsigned int) (((double)pthread_rand_np(&(this->rseed)) /
336 ((double)RAND_MAX + 1) * my.delay ) + .5)
337 );
338 } else if (my.delay >= .001) {
339 pthread_usleep_np(
340 (unsigned int) (((double)pthread_rand_np(&(this->rseed)) /
341 ((double)RAND_MAX + 1) * my.delay * 1000000 ) + .0005)
342 );
343 }
344
345 if (my.failures > 0 && my.failed >= my.failures) {
346 break;
347 }
348 }
349
350 #ifdef SIGNAL_CLIENT_PLATFORM
351 #else/*CANCEL_CLIENT_PLATFORM*/
352 // XXX: every cleanup must have a pop
353 pthread_cleanup_pop(0);
354 #endif/*SIGNAL_CLIENT_PLATFORM*/
355
356 if (this->conn->sock >= 0){
357 this->conn->connection.reuse = 0;
358 socket_close(this->conn);
359 }
360 this->conn->page = page_destroy(this->conn->page);
361 this->conn->cache = cache_destroy(this->conn->cache); //XXX: do we want to persist this?
362 xfree(this->conn);
363 this->conn = NULL;
364
365 return NULL;
366 }
367
368 void
browser_set_urls(BROWSER this,ARRAY urls)369 browser_set_urls(BROWSER this, ARRAY urls)
370 {
371 this->urls = urls;
372 }
373
374 void
browser_set_cookies(BROWSER this,HASH cookies)375 browser_set_cookies(BROWSER this, HASH cookies)
376 {
377 int i = 0;
378
379 this->cookies = cookies;
380
381 if (this->cookies != NULL) {
382 char **keys = hash_get_keys(this->cookies);
383 for (i = 0; i < hash_get_entries(this->cookies); i ++){
384 /**
385 * We need a local copy of the variable to pass to cookies_add
386 */
387 char *tmp;
388 int len = strlen(hash_get(this->cookies, keys[i]));
389 tmp = xmalloc(len+2);
390 memset(tmp, '\0', len+2);
391 snprintf(tmp, len+1, "%s", (char*)hash_get(this->cookies, keys[i]));
392 cookies_add(my.cookies, tmp, ".");
393 xfree(tmp);
394 }
395 }
396 }
397
398 private BOOLEAN
__request(BROWSER this,URL U)399 __request(BROWSER this, URL U) {
400 this->conn->scheme = url_get_scheme(U);
401
402 switch (this->conn->scheme) {
403 case FTP:
404 return __ftp(this, U);
405 case HTTP:
406 case HTTPS:
407 default:
408 return __http(this, U);
409 }
410 }
411
412 /**
413 * HTTP client request.
414 * The protocol is executed in http.c
415 * This function invoked functions inside that module
416 * and it gathers statistics about the request.
417 */
418 private BOOLEAN
__http(BROWSER this,URL U)419 __http(BROWSER this, URL U)
420 {
421 unsigned long bytes = 0;
422 int code, okay, fail;
423 float etime;
424 clock_t start, stop;
425 struct tms t_start, t_stop;
426 RESPONSE resp;
427 char *meta = NULL;
428 #ifdef HAVE_LOCALTIME_R
429 struct tm keepsake;
430 #endif/*HAVE_LOCALTIME_R*/
431 time_t now;
432 struct tm *tmp;
433 size_t len;
434 char fmtime[65];
435 URL redirect_url = NULL;
436
437 page_clear(this->conn->page);
438
439 if (my.csv) {
440 now = time(NULL);
441 #ifdef HAVE_LOCALTIME_R
442 tmp = (struct tm *)localtime_r(&now, &keepsake);
443 #else
444 tmp = localtime(&now);
445 #endif/*HAVE_LOCALTIME_R*/
446 if (tmp) {
447 len = strftime(fmtime, 64, "%Y-%m-%d %H:%M:%S", tmp);
448 if (len == 0) {
449 memset(fmtime, '\0', 64);
450 snprintf(fmtime, 64, "n/a");
451 }
452 } else {
453 snprintf(fmtime, 64, "n/a");
454 }
455 }
456
457 if (url_get_scheme(U) == UNSUPPORTED) {
458 if (my.verbose && !my.get && !my.print) {
459 NOTIFY (
460 ERROR,
461 "%s %d %6.2f secs: %7d bytes ==> %s\n",
462 "UNSPPRTD", 501, 0.00, 0, "PROTOCOL NOT SUPPORTED BY SIEGE"
463 );
464 } /* end if my.verbose */
465 return FALSE;
466 }
467
468 /* record transaction start time */
469 start = times(&t_start);
470 if (! __init_connection(this, U)) return FALSE;
471
472 /**
473 * write to socket with a GET/POST/PUT/DELETE/HEAD
474 */
475 if (url_get_method(U) == POST || url_get_method(U) == PUT || url_get_method(U) == PATCH ||
476 url_get_method(U) == DELETE || url_get_method(U) == OPTIONS) {
477 if ((http_post(this->conn, U)) == FALSE) {
478 this->conn->connection.reuse = 0;
479 socket_close(this->conn);
480 return FALSE;
481 }
482 } else {
483 if ((http_get(this->conn, U)) == FALSE) {
484 this->conn->connection.reuse = 0;
485 socket_close(this->conn);
486 return FALSE;
487 }
488 }
489
490 /**
491 * read from socket and collect statistics.
492 */
493 if ((resp = http_read_headers(this->conn, U))==NULL) {
494 this->conn->connection.reuse = 0;
495 socket_close(this->conn);
496 echo ("%s:%d NULL headers", __FILE__, __LINE__);
497 return FALSE;
498 }
499
500 code = response_get_code(resp);
501
502 if (code == 418) {
503 /**
504 * I don't know what server we're talking to but I
505 * know what it's not. It's not an HTTP server....
506 */
507 this->conn->connection.reuse = 0;
508 socket_close(this->conn);
509 stop = times(&t_stop);
510 etime = elapsed_time(stop - start);
511 this->hits ++;
512 this->time += etime;
513 this->fail += 1;
514
515 __display_result(this, resp, U, 0, etime);
516 resp = response_destroy(resp);
517 return FALSE;
518 }
519
520 bytes = http_read(this->conn, resp);
521 if (my.print) {
522 printf("%s\n", page_value(this->conn->page));
523 }
524
525 if (my.parser == TRUE) {
526 if (strmatch(response_get_content_type(resp), "text/html") && code < 300) {
527 int i;
528 html_parser(this->parts, U, page_value(this->conn->page));
529 for (i = 0; i < (int)array_length(this->parts); i++) {
530 URL url = (URL)array_get(this->parts, i);
531 if (url_is_redirect(url)) {
532 URL tmp = (URL)array_remove(this->parts, i);
533 meta = xstrdup(url_get_absolute(tmp));
534 tmp = url_destroy(tmp);
535 }
536 }
537 }
538 }
539
540 if (!my.zero_ok && (bytes < 1)) {
541 this->conn->connection.reuse = 0;
542 socket_close(this->conn);
543 resp = response_destroy(resp);
544 echo ("%s:%d zero bytes back from server", __FILE__, __LINE__);
545 return FALSE;
546 }
547 stop = times(&t_stop);
548 etime = elapsed_time(stop - start);
549 okay = response_success(resp);
550 fail = response_failure(resp);
551 /**
552 * quantify the statistics for this client.
553 */
554 this->bytes += bytes;
555 this->time += etime;
556 this->code += okay;
557 this->fail += fail;
558 if (code == 200) {
559 this->okay++;
560 }
561
562 /**
563 * check to see if this transaction is the longest or shortest
564 */
565 if (etime > __himark) {
566 __himark = etime;
567 }
568 if ((__lomark < 0) || (etime < __lomark)) {
569 __lomark = etime;
570 }
571 this->himark = __himark;
572 this->lomark = __lomark;
573
574 /**
575 * verbose output, print statistics to stdout
576 */
577 __display_result(this, resp, U, bytes, etime);
578
579 /**
580 * close the socket and free memory.
581 */
582 if (!my.keepalive) {
583 socket_close(this->conn);
584 }
585
586 switch (code) {
587 case 200:
588 if (meta != NULL && strlen(meta) > 2) {
589 /**
590 * <meta http-equiv="refresh" content="0; url=https://www.joedog.org/haha.html" />
591 */
592 redirect_url = url_normalize(U, meta);
593 xfree(meta);
594 meta = NULL;
595 page_clear(this->conn->page);
596 if (empty(url_get_hostname(redirect_url))) {
597 url_set_hostname(redirect_url, url_get_hostname(U));
598 }
599 url_set_redirect(U, FALSE);
600 url_set_redirect(redirect_url, FALSE);
601 if ((__request(this, redirect_url)) == FALSE) {
602 redirect_url = url_destroy(redirect_url);
603 return FALSE;
604 }
605 redirect_url = url_destroy(redirect_url);
606 }
607 break;
608 case 201:
609 if (my.follow && response_get_location(resp) != NULL) {
610 redirect_url = url_normalize(U, response_get_location(resp));
611 if (empty(url_get_hostname(redirect_url))) {
612 url_set_hostname(redirect_url, url_get_hostname(U));
613 }
614 if ((__request(this, redirect_url)) == FALSE) {
615 redirect_url = url_destroy(redirect_url);
616 return FALSE;
617 }
618 }
619 break;
620 case 301:
621 case 302:
622 case 303:
623 case 307:
624 if (my.follow && response_get_location(resp) != NULL) {
625 /**
626 * XXX: What if the server sends us
627 * Location: path/file.htm
628 * OR
629 * Location: /path/file.htm
630 */
631 redirect_url = url_normalize(U, response_get_location(resp));
632
633 if (empty(url_get_hostname(redirect_url))) {
634 url_set_hostname(redirect_url, url_get_hostname(U));
635 }
636 if (code == 307) {
637 url_set_conttype(redirect_url,url_get_conttype(U));
638 url_set_method(redirect_url, url_get_method(U));
639
640 if (url_get_method(redirect_url) == POST || url_get_method(redirect_url) == PUT ||
641 url_get_method(redirect_url) == PATCH || url_get_method(U) == DELETE ||
642 url_get_method(U) == OPTIONS) {
643 url_set_postdata(redirect_url, url_get_postdata(U), url_get_postlen(U));
644 }
645 }
646 if ((__request(this, redirect_url)) == FALSE) {
647 redirect_url = url_destroy(redirect_url);
648 return FALSE;
649 }
650 }
651 redirect_url = url_destroy(redirect_url);
652 break;
653 case 401:
654 /**
655 * WWW-Authenticate challenge from the WWW server
656 */
657 this->auth.www = (this->auth.www==0) ? 1 : this->auth.www;
658 if ((this->auth.bids.www++) < my.bids - 1) {
659 BOOLEAN b;
660 if (response_get_www_auth_type(resp) == DIGEST) {
661 this->auth.type.www = DIGEST;
662 b = auth_set_digest_header(
663 my.auth, &(this->auth.wchlg), &(this->auth.wcred), &(this->rseed),
664 response_get_www_auth_realm(resp), response_get_www_auth_challenge(resp)
665 );
666 if (b == FALSE) {
667 fprintf(stderr, "ERROR: Unable to respond to an authorization challenge\n");
668 fprintf(stderr, " in the following realm: '%s'\n", response_get_www_auth_realm(resp));
669 fprintf(stderr, " Did you set login credentials in the conf file?\n");
670 resp = response_destroy(resp);
671 return FALSE;
672 }
673 }
674 if (response_get_www_auth_type(resp) == NTLM) {
675 this->auth.type.www = NTLM;
676 b = auth_set_ntlm_header (
677 my.auth, HTTP, response_get_www_auth_challenge(resp), response_get_www_auth_realm(resp)
678 );
679 }
680 if (response_get_www_auth_type(resp) == BASIC) {
681 this->auth.type.www = BASIC;
682 auth_set_basic_header(my.auth, HTTP, response_get_www_auth_realm(resp));
683 }
684 if ((__request(this, U)) == FALSE) {
685 fprintf(stderr, "ERROR from http_request\n");
686 return FALSE;
687 }
688 }
689 break;
690 case 407:
691 /**
692 * Proxy-Authenticate challenge from the proxy server.
693 */
694 this->auth.proxy = (this->auth.proxy==0) ? 1 : this->auth.proxy;
695 if ((this->auth.bids.proxy++) < my.bids - 1) {
696 if (response_get_proxy_auth_type(resp) == DIGEST) {
697 BOOLEAN b;
698 this->auth.type.proxy = DIGEST;
699 b = auth_set_digest_header (
700 my.auth, &(this->auth.pchlg), &(this->auth.pcred), &(this->rseed),
701 response_get_proxy_auth_realm(resp), response_get_proxy_auth_challenge(resp)
702 );
703 if (b == FALSE) {
704 fprintf(stderr, "ERROR: Unable to respond to a proxy authorization challenge\n");
705 fprintf(stderr, " in the following HTTP realm: '%s'\n", response_get_proxy_auth_realm(resp));
706 fprintf(stderr, " Did you set proxy-login credentials in the conf file?\n");
707 resp = response_destroy(resp);
708 return FALSE;
709 }
710 }
711 if (response_get_proxy_auth_type(resp) == BASIC) {
712 this->auth.type.proxy = BASIC;
713 auth_set_basic_header(my.auth, PROXY, response_get_proxy_auth_realm(resp));
714 }
715 if ((__request(this, U)) == FALSE)
716 return FALSE;
717 }
718 break;
719 case 408:
720 case 500:
721 case 501:
722 case 502:
723 case 503:
724 case 504:
725 case 505:
726 case 506:
727 case 507:
728 case 508:
729 case 509:
730 return FALSE;
731 default:
732 break;
733 }
734
735 this->hits++;
736 resp = response_destroy(resp);
737
738 return TRUE;
739 }
740
741 /**
742 * HTTP client request.
743 * The protocol is executed in http.c
744 * This function invoked functions inside that module
745 * and it gathers statistics about the request.
746 */
747 private BOOLEAN
__ftp(BROWSER this,URL U)748 __ftp(BROWSER this, URL U)
749 {
750 int pass;
751 int fail;
752 int code = 0; // capture the relevent return code
753 float etime; // elapsed time
754 CONN *D = NULL; // FTP data connection
755 size_t bytes = 0; // bytes from server
756 clock_t start, stop;
757 struct tms t_start, t_stop;
758
759 D = xcalloc(sizeof(CONN), 1);
760 D->sock = -1;
761
762 if (! __init_connection(this, U)) {
763 NOTIFY (
764 ERROR, "%s:%d connection failed %s:%d",
765 __FILE__, __LINE__, url_get_hostname(U), url_get_port(U)
766 );
767 xfree(D);
768 return FALSE;
769 }
770
771 start = times(&t_start);
772 if (this->conn->sock < 0) {
773 NOTIFY (
774 ERROR, "%s:%d connection failed %s:%d",
775 __FILE__, __LINE__, url_get_hostname(U), url_get_port(U)
776 );
777 socket_close(this->conn);
778 xfree(D);
779 return FALSE;
780 }
781
782 if (url_get_username(U) == NULL || strlen(url_get_username(U)) < 1) {
783 url_set_username(U, auth_get_ftp_username(my.auth, url_get_hostname(U)));
784 }
785
786 if (url_get_password(U) == NULL || strlen(url_get_password(U)) < 1) {
787 url_set_password(U, auth_get_ftp_password(my.auth, url_get_hostname(U)));
788 }
789
790 if (ftp_login(this->conn, U) == FALSE) {
791 if (my.verbose) {
792 int color = __select_color(this->conn->ftp.code);
793 DISPLAY (
794 color, "FTP/%d %6.2f secs: %7lu bytes ==> %-6s %s",
795 this->conn->ftp.code, 0.0, bytes, url_get_method_name(U), url_get_request(U)
796 );
797 }
798 xfree(D);
799 this->fail += 1;
800 return FALSE;
801 }
802
803 ftp_pasv(this->conn);
804 if (this->conn->ftp.pasv == TRUE) {
805 debug("Connecting to: %s:%d", this->conn->ftp.host, this->conn->ftp.port);
806 D->sock = new_socket(D, this->conn->ftp.host, this->conn->ftp.port);
807 if (D->sock < 0) {
808 debug (
809 "%s:%d connection failed. error %d(%s)",__FILE__, __LINE__, errno,strerror(errno)
810 );
811 this->fail += 1;
812 socket_close(D);
813 xfree(D);
814 return FALSE;
815 }
816 }
817 if (url_get_method(U) == POST || url_get_method(U) == PUT || url_get_method(U) == PATCH ||
818 url_get_method(U) == DELETE || url_get_method(U) == OPTIONS) {
819 ftp_stor(this->conn, U);
820 bytes = ftp_put(D, U);
821 code = this->conn->ftp.code;
822 } else {
823 if (ftp_size(this->conn, U) == TRUE) {
824 if (ftp_retr(this->conn, U) == TRUE) {
825 bytes = ftp_get(D, U, this->conn->ftp.size);
826 }
827 }
828 code = this->conn->ftp.code;
829 }
830 socket_close(D);
831 ftp_quit(this->conn);
832
833 pass = (bytes == this->conn->ftp.size) ? 1 : 0;
834 fail = (pass == 0) ? 1 : 0;
835 stop = times(&t_stop);
836 etime = elapsed_time(stop - start);
837 this->bytes += bytes;
838 this->time += etime;
839 this->code += pass;
840 this->fail += fail;
841
842 /**
843 * check to see if this transaction is the longest or shortest
844 */
845 if (etime > __himark) {
846 __himark = etime;
847 }
848 if ((__lomark < 0) || (etime < __lomark)) {
849 __lomark = etime;
850 }
851 this->himark = __himark;
852 this->lomark = __lomark;
853
854 if (my.verbose) {
855 int color = (my.color == TRUE) ? __select_color(code) : -1;
856 DISPLAY (
857 color, "FTP/%d %6.2f secs: %7lu bytes ==> %-6s %s",
858 code, etime, bytes, url_get_method_name(U), url_get_request(U)
859 );
860 }
861 this->hits++;
862 xfree(D);
863 return TRUE;
864 }
865
866 private BOOLEAN
__init_connection(BROWSER this,URL U)867 __init_connection(BROWSER this, URL U)
868 {
869 this->conn->pos_ini = 0;
870 this->conn->inbuffer = 0;
871 this->conn->content.transfer = NONE;
872 this->conn->content.length = (size_t)~0L;// VL - issue #2, 0 is a legit.value
873 this->conn->connection.keepalive = (this->conn->connection.max==1)?0:my.keepalive;
874 this->conn->connection.reuse = (this->conn->connection.max==1)?0:my.keepalive;
875 this->conn->connection.tested = (this->conn->connection.tested==0)?1:this->conn->connection.tested;
876 this->conn->auth.www = this->auth.www;
877 this->conn->auth.wchlg = this->auth.wchlg;
878 this->conn->auth.wcred = this->auth.wcred;
879 this->conn->auth.proxy = this->auth.proxy;
880 this->conn->auth.pchlg = this->auth.pchlg;
881 this->conn->auth.pcred = this->auth.pcred;
882 this->conn->auth.type.www = this->auth.type.www;
883 this->conn->auth.type.proxy = this->auth.type.proxy;
884 memset(this->conn->buffer, 0, sizeof(this->conn->buffer));
885
886 debug (
887 "%s:%d attempting connection to %s:%d",
888 __FILE__, __LINE__,
889 (auth_get_proxy_required(my.auth))?auth_get_proxy_host(my.auth):url_get_hostname(U),
890 (auth_get_proxy_required(my.auth))?auth_get_proxy_port(my.auth):url_get_port(U)
891 );
892
893 if (!this->conn->connection.reuse || this->conn->connection.status == 0) {
894 if (auth_get_proxy_required(my.auth)) {
895 debug (
896 "%s:%d creating new socket: %s:%d",
897 __FILE__, __LINE__, auth_get_proxy_host(my.auth), auth_get_proxy_port(my.auth)
898 );
899 this->conn->sock = new_socket(this->conn, auth_get_proxy_host(my.auth), auth_get_proxy_port(my.auth));
900 } else {
901 debug (
902 "%s:%d creating new socket: %s:%d",
903 __FILE__, __LINE__, url_get_hostname(U), url_get_port(U)
904 );
905 this->conn->sock = new_socket(this->conn, url_get_hostname(U), url_get_port(U));
906 }
907 }
908
909 if (my.keepalive) {
910 this->conn->connection.reuse = TRUE;
911 }
912
913 if (this->conn->sock < 0) {
914 debug (
915 "%s:%d connection failed. error %d(%s)",__FILE__, __LINE__, errno,strerror(errno)
916 );
917 socket_close(this->conn);
918 return FALSE;
919 }
920
921 debug (
922 "%s:%d good socket connection: %s:%d",
923 __FILE__, __LINE__,
924 (auth_get_proxy_required(my.auth))?auth_get_proxy_host(my.auth):url_get_hostname(U),
925 (auth_get_proxy_required(my.auth))?auth_get_proxy_port(my.auth):url_get_port(U)
926 );
927
928 if (url_get_scheme(U) == HTTPS) {
929 if (auth_get_proxy_required(my.auth)) {
930 https_tunnel_request(this->conn, url_get_hostname(U), url_get_port(U));
931 https_tunnel_response(this->conn);
932 }
933 this->conn->encrypt = TRUE;
934 if (SSL_initialize(this->conn, url_get_hostname(U))==FALSE) {
935 return FALSE;
936 }
937 }
938 return TRUE;
939 }
940
941 private void
__display_result(BROWSER this,RESPONSE resp,URL U,unsigned long bytes,float etime)942 __display_result(BROWSER this, RESPONSE resp, URL U, unsigned long bytes, float etime)
943 {
944 char fmtime[65];
945 #ifdef HAVE_LOCALTIME_R
946 struct tm keepsake;
947 #endif/*HAVE_LOCALTIME_R*/
948 time_t now;
949 struct tm * tmp;
950 size_t len;
951
952
953 if (my.csv) {
954 now = time(NULL);
955 #ifdef HAVE_LOCALTIME_R
956 tmp = (struct tm *)localtime_r(&now, &keepsake);
957 #else
958 tmp = localtime(&now);
959 #endif/*HAVE_LOCALTIME_R*/
960 if (tmp) {
961 len = strftime(fmtime, 64, "%Y-%m-%d %H:%M:%S", tmp);
962 if (len == 0) {
963 memset(fmtime, '\0', 64);
964 snprintf(fmtime, 64, "n/a");
965 }
966 } else {
967 snprintf(fmtime, 64, "n/a");
968 }
969 }
970
971 /**
972 * verbose output, print statistics to stdout
973 */
974 if ((my.verbose && !my.get && !my.print) && (!my.debug)) {
975 int color = (my.color == TRUE) ? __select_color(response_get_code(resp)) : -1;
976 DATE date = new_date(NULL);
977 char *stamp = (my.timestamp)?date_stamp(date):"";
978 char *cached = response_get_from_cache(resp) ? "(C)":" ";
979 if (my.color && response_get_from_cache(resp) == TRUE) {
980 color = GREEN;
981 }
982
983 if (my.csv) {
984 if (my.display)
985 DISPLAY(color, "%s%s%s%4d,%s,%d,%6.2f,%7lu,%s,%d,%s",
986 stamp, (my.mark)?my.markstr:"", (my.mark)?",":"", this->id, response_get_protocol(resp),
987 response_get_code(resp), etime, bytes, url_get_display(U), url_get_ID(U), fmtime
988 );
989 else
990 DISPLAY(color, "%s%s%s%s,%d,%6.2f,%7lu,%s,%d,%s",
991 stamp, (my.mark)?my.markstr:"", (my.mark)?",":"", response_get_protocol(resp),
992 response_get_code(resp), etime, bytes, url_get_display(U), url_get_ID(U), fmtime
993 );
994 } else {
995 if (my.display)
996 DISPLAY(
997 color, "%4d) %s %d %6.2f secs: %7lu bytes ==> %-4s %s",
998 this->id, response_get_protocol(resp), response_get_code(resp),
999 etime, bytes, url_get_method_name(U), url_get_display(U)
1000 );
1001 else
1002 DISPLAY (
1003 color, "%s%s %d%s %5.2f secs: %7lu bytes ==> %-4s %s",
1004 stamp, response_get_protocol(resp), response_get_code(resp), cached,
1005 etime, bytes, url_get_method_name(U), url_get_display(U)
1006 );
1007 } /* else not my.csv */
1008 date = date_destroy(date);
1009 }
1010 return;
1011 }
1012
1013 private void
__increment_failures()1014 __increment_failures()
1015 {
1016 pthread_mutex_lock(&(my.lock));
1017 my.failed++;
1018 pthread_mutex_unlock(&(my.lock));
1019 pthread_testcancel();
1020 }
1021
1022 private BOOLEAN
__no_follow(const char * hostname)1023 __no_follow(const char *hostname)
1024 {
1025 int i;
1026
1027 for (i = 0; i < my.nomap->index; i++) {
1028 if (stristr(hostname, my.nomap->line[i]) != NULL) {
1029 return TRUE;
1030 }
1031 }
1032 return FALSE;
1033 }
1034
1035
1036 private int
__select_color(int code)1037 __select_color(int code)
1038 {
1039 switch(code) {
1040 case 150:
1041 case 200:
1042 case 201:
1043 case 202:
1044 case 203:
1045 case 204:
1046 case 205:
1047 case 206:
1048 case 226:
1049 return BLUE;
1050 case 300:
1051 case 301:
1052 case 302:
1053 case 303:
1054 case 304:
1055 case 305:
1056 case 306:
1057 case 307:
1058 return CYAN;
1059 case 400:
1060 case 401:
1061 case 402:
1062 case 403:
1063 case 404:
1064 case 405:
1065 case 406:
1066 case 407:
1067 case 408:
1068 case 409:
1069 case 410:
1070 case 411:
1071 case 412:
1072 case 413:
1073 case 414:
1074 case 415:
1075 case 416:
1076 case 417:
1077 return MAGENTA;
1078 case 500:
1079 case 501:
1080 case 502:
1081 case 503:
1082 case 504:
1083 case 505:
1084 default: // WTF?
1085 return RED;
1086 }
1087 return RED;
1088 }
1089
1090 #ifdef SIGNAL_CLIENT_PLATFORM
1091 private void
__signal_handler(int sig)1092 __signal_handler(int sig)
1093 {
1094 pthread_exit(&sig);
1095 }
1096
1097 private void
__signal_init()1098 __signal_init()
1099 {
1100 struct sigaction sa;
1101
1102 sa.sa_handler = signal_handler;
1103 sigemptyset(&sa.sa_mask);
1104 sa.sa_flags = 0;
1105 sigaction(SIGUSR1, &sa, NULL);
1106 }
1107 #else/*CANCEL_CLIENT_PLATFORM*/
1108 private void
__signal_cleanup()1109 __signal_cleanup()
1110 {
1111 return;
1112 }
1113 #endif
1114