]> git.ipfire.org Git - thirdparty/squid.git/blame - test-suite/tcp-banger2.c
Source Format Enforcement (#532)
[thirdparty/squid.git] / test-suite / tcp-banger2.c
CommitLineData
4e0938ef 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
4e0938ef
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
f7f3304a 9#include "squid.h"
6a54c60e 10
56792124 11/*
12 * On some systems, FD_SETSIZE is set to something lower than the
13 * actual number of files which can be opened. IRIX is one case,
14 * NetBSD is another. So here we increase FD_SETSIZE to our
15 * configure-discovered maximum *before* any system includes.
16 */
17#define CHANGE_FD_SETSIZE 1
18
19/* Cannot increase FD_SETSIZE on Linux */
8a09e810 20#if _SQUID_LINUX_
56792124 21#undef CHANGE_FD_SETSIZE
22#define CHANGE_FD_SETSIZE 0
23#endif
24
25/* Cannot increase FD_SETSIZE on FreeBSD before 2.2.0, causes select(2)
26 * to return EINVAL. */
27/* Marian Durkovic <marian@svf.stuba.sk> */
28/* Peter Wemm <peter@spinner.DIALix.COM> */
8a09e810 29#if _SQUID_FREEBSD_
56792124 30#include <osreldate.h>
31#if __FreeBSD_version < 220000
32#undef CHANGE_FD_SETSIZE
33#define CHANGE_FD_SETSIZE 0
34#endif
35#endif
58a96f63 36
37/* Increase FD_SETSIZE if SQUID_MAXFD is bigger */
38#if CHANGE_FD_SETSIZE && SQUID_MAXFD > DEFAULT_FD_SETSIZE
39#define FD_SETSIZE SQUID_MAXFD
40#endif
41
42#if HAVE_UNISTD_H
735246e8 43#include <unistd.h>
58a96f63 44#endif
58a96f63 45#if HAVE_FCNTL_H
735246e8 46#include <fcntl.h>
58a96f63 47#endif
32d002cb 48#if HAVE_STRING_H
735246e8 49#include <string.h>
58a96f63 50#endif
32d002cb 51#if HAVE_STRINGS_H
735246e8 52#include <strings.h>
58a96f63 53#endif
58a96f63 54#if HAVE_SIGNAL_H
735246e8 55#include <signal.h>
58a96f63 56#endif
57#if HAVE_TIME_H
735246e8 58#include <time.h>
58a96f63 59#endif
58a96f63 60#if HAVE_SYS_SOCKET_H
735246e8 61#include <sys/socket.h>
58a96f63 62#endif
63#if HAVE_NETINET_IN_H
735246e8 64#include <netinet/in.h>
58a96f63 65#endif
66#if HAVE_ARPA_INET_H
735246e8 67#include <arpa/inet.h>
58a96f63 68#endif
69#if HAVE_ERRNO_H
735246e8 70#include <errno.h>
58a96f63 71#endif
14ff08f1 72#if HAVE_SYS_STAT_H
73#include <sys/stat.h>
74#endif
75#if HAVE_ASSERT_H
76#include <assert.h>
77#endif
b6a2f15e 78#if HAVE_CTYPE_H
79#include <ctype.h>
80#endif
735246e8 81
82#define PROXY_PORT 3128
83#define PROXY_ADDR "127.0.0.1"
735246e8 84#define READ_BUF_SZ 4096
85
86static int proxy_port = PROXY_PORT;
87static char *proxy_addr = PROXY_ADDR;
88static char *progname;
89static int reqpersec;
90static int nrequests;
91static int opt_ims = 0;
c4248266 92static int opt_range = 0;
6f821399 93static int opt_accel = 0;
735246e8 94static int max_connections = 64;
33fda8cd 95static time_t lifetime = 60;
15dd95c1 96static time_t process_lifetime = 86400;
33fda8cd 97static struct timeval now;
56792124 98static long long total_bytes_written = 0;
99static long long total_bytes_read = 0;
14ff08f1 100static int opt_checksum = 0;
56792124 101static char *custom_header = NULL;
ed2ab7db 102FILE *trace_file = NULL;
735246e8 103
104typedef void (CB) (int, void *);
105
106struct _f {
107 CB *cb;
14ff08f1 108 CB *ccb;
735246e8 109 void *data;
33fda8cd 110 time_t start;
735246e8 111};
14ff08f1 112struct _request {
113 int fd;
7d97d03c 114 char *url;
ed2ab7db 115 char method[16];
116 char requestbodyfile[256];
14ff08f1 117 char buf[READ_BUF_SZ * 2 + 1];
118 int headfound;
2444b689 119 int validsize;
120 int bodysize;
121 int content_length;
55095043 122 int status;
14ff08f1 123 long validsum;
14ff08f1 124 long sum;
56792124 125 int validstatus;
14ff08f1 126};
735246e8 127
b61d6f3d 128struct _f FD[SQUID_MAXFD];
735246e8 129int nfds = 0;
130int maxfd = 0;
131
7d97d03c 132static void
133free_request(struct _request *r)
134{
55095043 135 if (r->url)
26ac0430 136 free(r->url);
55095043 137 free(r);
7d97d03c 138}
139
528b2c61 140#define RFC1123_STRFTIME "%a, %d %b %Y %H:%M:%S GMT"
735246e8 141char *
528b2c61 142mkrfc1123(t)
26ac0430 143time_t *t;
735246e8 144{
145 static char buf[128];
146 struct tm *gmt = gmtime(t);
147 buf[0] = '\0';
528b2c61 148 (void) strftime(buf, 127, RFC1123_STRFTIME, gmt);
735246e8 149 return buf;
150}
151
152void
153fd_close(int fd)
154{
155 close(fd);
14ff08f1 156 if (FD[fd].ccb)
26ac0430 157 FD[fd].ccb(fd, FD[fd].data);
14ff08f1 158 FD[fd].ccb = NULL;
735246e8 159 FD[fd].cb = NULL;
160 FD[fd].data = NULL;
161 nfds--;
162 if (fd == maxfd) {
26ac0430
AJ
163 while (fd > 0 && FD[fd].cb == NULL)
164 fd--;
165 maxfd = fd;
735246e8 166 }
167}
168
169void
3cd80ef8 170fd_open(int fd, CB * cb, void *data, CB * ccb)
735246e8 171{
b61d6f3d 172 assert(fd < SQUID_MAXFD);
735246e8 173 FD[fd].cb = cb;
14ff08f1 174 FD[fd].ccb = ccb;
735246e8 175 FD[fd].data = data;
33fda8cd 176 FD[fd].start = now.tv_sec;
735246e8 177 if (fd > maxfd)
26ac0430 178 maxfd = fd;
735246e8 179 nfds++;
180}
181
182void
183sig_intr(int sig)
184{
6a54c60e 185 fd_close(0);
14ff08f1 186 nfds++;
6a54c60e 187 printf("\rWaiting for open connections to finish...\n");
188 signal(sig, SIG_DFL);
735246e8 189}
190
6a54c60e 191void
735246e8 192read_reply(int fd, void *data)
193{
14ff08f1 194 struct _request *r = data;
195 static unsigned char buf[READ_BUF_SZ];
196 int len;
1707acbe 197 int used = 0;
3cd80ef8 198 if ((len = read(fd, buf, READ_BUF_SZ)) <= 0) {
26ac0430
AJ
199 fd_close(fd);
200 reqpersec++;
201 nrequests++;
202 return;
1707acbe 203 }
204 total_bytes_read += len;
205 if (r->headfound < 2) {
26ac0430
AJ
206 char *p, *header = NULL;
207 int oldlen = strlen(r->buf);
208 int newlen = oldlen + len;
209 assert(oldlen <= READ_BUF_SZ);
210 memcpy(r->buf + oldlen, buf, len);
211 r->buf[newlen + 1] = '\0';
212 for (p = r->buf; r->headfound < 2 && used < newlen; p++, used++) {
213 switch (*p) {
214 case '\n':
215 r->headfound++;
216 if (!header)
217 break;
218 /* Decode header */
219 if (strncmp(header, "HTTP", 4) == 0)
220 r->status = atoi(header + 8);
221 else if (strncasecmp(header, "Content-Length:", 15) == 0)
222 r->content_length = atoi(header + 15);
223 else if (strncasecmp(header, "X-Request-URI:", 14) == 0) {
224 /* Check URI */
225 if (strncmp(r->url, header + 15, strcspn(header + 15, "\r\n"))) {
226 char url[8192];
227 strncpy(url, header + 15, strcspn(header + 15, "\r\n"));
228 url[strcspn(header + 15, "\r\n")] = '\n';
229 fprintf(stderr, "ERROR: Sent %s received %s\n",
230 r->url, url);
231 }
232 }
233 header = NULL;
234 break;
235 case '\r':
236 break;
237 default:
238 r->headfound = 0;
239 if (!header)
240 header = p;
241 break;
242 }
243 }
244 if (header) {
245 memmove(r->buf, header, newlen - (header - r->buf) + 1);
246 }
247 assert(used >= oldlen);
248 used -= oldlen;
1707acbe 249 }
250 r->bodysize += len - used;
251 if (opt_checksum) {
26ac0430
AJ
252 for (; used < len; used++) {
253 r->sum += buf[used];
254 }
6a54c60e 255 }
735246e8 256}
257
14ff08f1 258void
259reply_done(int fd, void *data)
735246e8 260{
14ff08f1 261 struct _request *r = data;
f53969cc 262 if (opt_range); /* skip size checks for now */
56792124 263 else if (strcmp(r->method, "HEAD") == 0);
2444b689 264 else if (r->bodysize != r->content_length && r->content_length >= 0)
26ac0430
AJ
265 fprintf(stderr, "ERROR: %s got %d of %d bytes\n",
266 r->url, r->bodysize, r->content_length);
14ff08f1 267 else if (r->validsize >= 0) {
26ac0430
AJ
268 if (r->validsize != r->bodysize)
269 fprintf(stderr, "WARNING: %s size mismatch wanted %d bytes got %d\n",
270 r->url, r->validsize, r->bodysize);
271 else if (opt_checksum && r->validsum != r->sum)
272 fprintf(stderr, "WARNING: %s invalid checksum wanted 0x%lx got 0x%lx\n",
273 r->url, r->validsum, r->sum);
ed2ab7db 274 }
56792124 275 if (r->validstatus && r->status != r->validstatus) {
26ac0430
AJ
276 fprintf(stderr, "WARNING: %s status mismatch wanted %d got %d\n",
277 r->url, r->validstatus, r->status);
56792124 278 }
ed2ab7db 279 if (trace_file) {
26ac0430
AJ
280 if (opt_checksum)
281 fprintf(trace_file, "%s %s %d %s %d 0x%lx\n",
282 r->method, r->url, r->status, r->requestbodyfile, r->bodysize, r->sum);
283 else
284 fprintf(trace_file, "%s %s %d %s %d\n",
285 r->method, r->url, r->status, r->requestbodyfile, r->bodysize);
14ff08f1 286 }
7d97d03c 287 free_request(r);
14ff08f1 288}
289
290struct _request *
e1381638 291request(char *urlin) {
3cd80ef8 292 int s = -1, f = -1;
735246e8 293 char buf[4096];
14ff08f1 294 char msg[8192];
56792124 295 char *method, *url, *file, *size, *checksum, *status;
6f821399 296 char *host;
14ff08f1 297 char urlbuf[8192];
3cd80ef8 298 int len, len2;
735246e8 299 time_t w;
14ff08f1 300 struct stat st;
735246e8 301 struct sockaddr_in S;
14ff08f1 302 struct _request *r;
206b24db 303 if (*urlin == '\0')
26ac0430 304 return NULL;
735246e8 305 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
26ac0430
AJ
306 perror("socket");
307 return NULL;
735246e8 308 }
309 memset(&S, '\0', sizeof(struct sockaddr_in));
310 S.sin_family = AF_INET;
311 S.sin_port = htons(proxy_port);
312 S.sin_addr.s_addr = inet_addr(proxy_addr);
6a54c60e 313 if (connect(s, (struct sockaddr *) &S, sizeof(S)) < 0) {
26ac0430
AJ
314 close(s);
315 perror("connect");
316 return NULL;
735246e8 317 }
3cd80ef8 318 strcpy(urlbuf, urlin);
319 method = strtok(urlbuf, " ");
320 url = strtok(NULL, " ");
56792124 321 status = strtok(NULL, " ");
3cd80ef8 322 file = strtok(NULL, " ");
323 size = strtok(NULL, " ");
324 checksum = strtok(NULL, " ");
14ff08f1 325 if (!url) {
26ac0430
AJ
326 url = method;
327 method = "GET";
14ff08f1 328 }
ed2ab7db 329 if (!file)
26ac0430 330 file = "-";
ed2ab7db 331 if (!size)
26ac0430 332 size = "-";
ed2ab7db 333 if (!checksum)
26ac0430 334 checksum = "-";
3cd80ef8 335 r = calloc(1, sizeof *r);
336 assert(r != NULL);
86c63190 337 r->url = xstrdup(url);
7d97d03c 338 assert(r->url);
ed2ab7db 339 strcpy(r->method, method);
340 strcpy(r->requestbodyfile, file);
14ff08f1 341 r->fd = s;
3cd80ef8 342 if (size && strcmp(size, "-") != 0)
26ac0430 343 r->validsize = atoi(size);
14ff08f1 344 else
f53969cc 345 r->validsize = -1; /* Unknown */
3cd80ef8 346 if (checksum && strcmp(checksum, "-") != 0)
26ac0430 347 r->validsum = strtoul(checksum, NULL, 0);
56792124 348 if (status)
26ac0430 349 r->validstatus = atoi(status);
f53969cc 350 r->content_length = -1; /* Unknown */
6f821399 351 if (opt_accel) {
26ac0430
AJ
352 host = strchr(url, '/') + 2;
353 url = strchr(host, '/');
6f821399 354 } else {
26ac0430 355 host = NULL;
6f821399 356 }
86c63190 357 snprintf(msg, sizeof(msg)-1, "%s %s HTTP/1.0\r\n", method, url);
6f821399 358 if (host) {
26ac0430 359 url[0] = '\0';
86c63190 360 snprintf(buf, sizeof(buf)-1, "Host: %s\r\n", host);
26ac0430
AJ
361 strcat(msg, buf);
362 url[0] = '/';
6f821399 363 }
14ff08f1 364 strcat(msg, "Accept: */*\r\n");
735246e8 365 if (opt_ims && (lrand48() & 0x03) == 0) {
26ac0430 366 w = time(NULL) - (lrand48() & 0x3FFFF);
86c63190 367 snprintf(buf, sizeof(buf)-1, "If-Modified-Since: %s\r\n", mkrfc850(&w));
26ac0430 368 strcat(msg, buf);
735246e8 369 }
3cd80ef8 370 if (file && strcmp(file, "-") != 0) {
26ac0430
AJ
371 f = open(file, O_RDONLY);
372 if (f < 0) {
373 perror("open file");
374 exit(1);
375 }
376 fstat(f, &st);
86c63190 377 snprintf(buf, sizeof(buf)-1, "Content-Length: %d\r\n", (int) st.st_size);
26ac0430 378 strcat(msg, buf);
14ff08f1 379 }
c4248266 380 if (opt_range && (lrand48() & 0x03) == 0) {
26ac0430
AJ
381 int len;
382 int count = 0;
383 strcat(msg, "Range: bytes=");
384 while (((len = (int) lrand48()) & 0x03) == 0 || !count) {
385 const int offset = (int) lrand48();
386 if (count)
387 strcat(msg, ",");
388 switch (lrand48() & 0x03) {
389 case 0:
86c63190 390 snprintf(buf, sizeof(buf)-1, "-%d", len);
26ac0430
AJ
391 break;
392 case 1:
86c63190 393 snprintf(buf, sizeof(buf)-1, "%d-", offset);
26ac0430
AJ
394 break;
395 default:
86c63190 396 snprintf(buf, sizeof(buf)-1, "%d-%d", offset, offset + len);
26ac0430
AJ
397 break;
398 }
399 strcat(msg, buf);
400 count++;
401 }
402 strcat(msg, "\r\n");
c4248266 403 }
56792124 404 strcat(msg, custom_header);
14ff08f1 405 strcat(msg, "\r\n");
406 len = strlen(msg);
3cd80ef8 407 if ((len2 = write(s, msg, len)) != len) {
26ac0430
AJ
408 close(s);
409 perror("write request");
410 free_request(r);
411 return NULL;
14ff08f1 412 } else
26ac0430 413 total_bytes_written += len2;
3cd80ef8 414 if (f >= 0) {
26ac0430
AJ
415 while ((len = read(f, buf, sizeof(buf))) > 0) {
416 len2 = write(s, buf, len);
417 if (len2 < 0) {
418 perror("write body");
419 close(s);
420 free_request(r);
421 }
422 }
423 if (len < 0) {
424 perror("read body");
425 exit(1);
426 }
735246e8 427 }
26ac0430
AJ
428 /*
429 * if (fcntl(s, F_SETFL, O_NDELAY) < 0)
430 * perror("fcntl O_NDELAY");
431 */
14ff08f1 432 return r;
735246e8 433}
434
435void
436read_url(int fd, void *junk)
437{
14ff08f1 438 struct _request *r;
735246e8 439 static char buf[8192];
440 char *t;
206b24db 441 buf[0] = '\0';
735246e8 442 if (fgets(buf, 8191, stdin) == NULL) {
26ac0430
AJ
443 printf("Done Reading URLS\n");
444 fd_close(0);
445 nfds++;
446 return;
735246e8 447 }
206b24db 448 if (buf[0] == '\0')
26ac0430 449 return;
735246e8 450 if ((t = strchr(buf, '\n')))
26ac0430 451 *t = '\0';
14ff08f1 452 r = request(buf);
453 if (!r) {
26ac0430
AJ
454 max_connections = nfds - 1;
455 printf("NOTE: max_connections set at %d\n", max_connections);
14ff08f1 456 } else {
26ac0430 457 fd_open(r->fd, read_reply, r, reply_done);
735246e8 458 }
735246e8 459}
460
461void
462usage(void)
463{
ed2ab7db 464 fprintf(stderr, "usage: %s: -p port -h host -n max\n", progname);
465 fprintf(stderr, " -t <tracefile> Save request trace\n");
466 fprintf(stderr, " -c Check checksum agains trace\n");
467 fprintf(stderr, " -i Send random If-Modified-Since times\n");
468 fprintf(stderr, " -l <seconds> Connection lifetime timeout (default 60)\n");
6f821399 469 fprintf(stderr, " -a Accelerator mode\n");
56792124 470 fprintf(stderr, " -H <string> Custom header\n");
735246e8 471}
472
6a54c60e 473int
735246e8 474main(argc, argv)
26ac0430
AJ
475int argc;
476char *argv[];
735246e8 477{
478 int i;
479 int c;
480 int dt;
14ff08f1 481 int j;
3cd80ef8 482 fd_set R, R2;
735246e8 483 struct timeval start;
735246e8 484 struct timeval last;
485 struct timeval to;
486 setbuf(stdout, NULL);
487 setbuf(stderr, NULL);
86c63190 488 progname = xstrdup(argv[0]);
14ff08f1 489 gettimeofday(&now, NULL);
490 start = last = now;
86c63190 491 custom_header = xstrdup("");
56792124 492 while ((c = getopt(argc, argv, "ap:h:H:n:icrl:L:t:")) != -1) {
26ac0430
AJ
493 switch (c) {
494 case 'a':
495 opt_accel = 1;
496 break;
497 case 'p':
498 proxy_port = atoi(optarg);
499 break;
500 case 'h':
86c63190 501 proxy_addr = xstrdup(optarg);
26ac0430
AJ
502 break;
503 case 'n':
504 max_connections = atoi(optarg);
505 break;
506 case 'i':
507 opt_ims = 1;
508 break;
509 case 'l':
510 lifetime = (time_t) atoi(optarg);
511 break;
512 case 'L':
513 process_lifetime = (time_t) atoi(optarg);
514 break;
515 case 'c':
516 opt_checksum = 1;
517 break;
518 case 't':
519 trace_file = fopen(optarg, "a");
520 assert(trace_file);
521 setbuf(trace_file, NULL);
522 break;
523 case 'r':
524 opt_range = 1;
525 break;
526 case 'H':
527 custom_header = realloc(custom_header, strlen(custom_header) + strlen(optarg) + 2 + 1);
528 strcat(custom_header, optarg);
529 strcat(custom_header, "\r\n");
530 break;
531 default:
532 usage();
533 return 1;
534 }
735246e8 535 }
14ff08f1 536 fd_open(0, read_url, NULL, NULL);
537 nfds--;
735246e8 538 signal(SIGINT, sig_intr);
5616e319 539 signal(SIGPIPE, SIG_IGN);
14ff08f1 540 FD_ZERO(&R2);
541 while (nfds || FD[0].cb) {
26ac0430
AJ
542 FD_ZERO(&R);
543 to.tv_sec = 0;
544 to.tv_usec = 100000;
545 if (nfds < max_connections && FD[0].cb)
546 FD_SET(0, &R);
547 for (i = 1; i <= maxfd; i++) {
548 if (FD[i].cb == NULL)
549 continue;
550 if (now.tv_sec - FD[i].start > lifetime) {
551 fprintf(stderr, "WARNING: fd %d lifetime timeout\n", i);
552 fd_close(i);
553 continue;
554 }
555 FD_SET(i, &R);
556 }
557 if (select(maxfd + 1, &R, NULL, NULL, &to) < 0) {
558 fprintf(stderr, "maxfd=%d\n", maxfd);
559 if (errno != EINTR)
560 perror("select");
561 continue;
562 }
563 gettimeofday(&now, NULL);
564 for (i = 0; i <= maxfd; i++) {
565 if (!FD_ISSET(i, &R))
566 continue;
567 FD[i].cb(i, FD[i].data);
568 if (nfds < max_connections && FD[0].cb) {
569 j = 0;
570 FD_SET(0, &R2);
571 to.tv_sec = 0;
572 to.tv_usec = 0;
573 if (select(1, &R2, NULL, NULL, &to) == 1)
574 FD[0].cb(0, FD[0].data);
575 }
576 }
577 if (now.tv_sec > last.tv_sec) {
578 last = now;
579 dt = (int) (now.tv_sec - start.tv_sec);
580 printf("T+ %6d: %9d req (%+4d), %4d conn, %3d/sec avg, %dmb, %dkb/sec avg\n",
581 dt,
582 nrequests,
583 reqpersec,
584 nfds,
585 (int) (nrequests / dt),
586 (int) (total_bytes_read / 1024 / 1024),
587 (int) (total_bytes_read / 1024 / dt));
588 reqpersec = 0;
589 /*
590 * if (dt > process_lifetime)
591 * exit(0);
592 */
593 }
735246e8 594 }
b6a2f15e 595 printf("Exiting normally\n");
735246e8 596 return 0;
597}
f53969cc 598