mirror of
https://framagit.org/bortzmeyer/echoping
synced 2024-11-10 19:10:34 +00:00
1543 lines
38 KiB
C
1543 lines
38 KiB
C
/*
|
|
* echoping : uses the TCP echo (or other TCP or UDP protocols)
|
|
* service to measure (roughly) response times.
|
|
*
|
|
* Written by Stephane Bortzmeyer <bortz@users.sourceforge.net>. See
|
|
* the AUTHORS file for other contributors.
|
|
*
|
|
* $Id$
|
|
*
|
|
* */
|
|
|
|
|
|
char *progname;
|
|
|
|
#include "echoping.h"
|
|
|
|
/*
|
|
* An option to define only if you want to drive echoping from another
|
|
* process. Useless but harmless otherwise. In practice, while OSF/1 is happy
|
|
* with it, SunOS refuses to use fflush on a NULL and Linux fails.
|
|
*/
|
|
#undef FLUSH_OUTPUT /* Not really supported, see the TODO */
|
|
|
|
/* Global variables for main and printstats */
|
|
|
|
int return_code = 0;
|
|
int rc;
|
|
unsigned int number = 1;
|
|
struct timeval max, min, total, median, stddev, temp;
|
|
struct timeval conntv, connectedtv, sendtv, recvtv;
|
|
unsigned int successes, attempts = 0;
|
|
unsigned int size = DEFLINE;
|
|
unsigned int j = 0;
|
|
|
|
int family = PF_UNSPEC;
|
|
|
|
struct result results[MAX_ITERATIONS];
|
|
struct timeval good_results[MAX_ITERATIONS];
|
|
extern int tvcmp();
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
const char *argv[];
|
|
{
|
|
|
|
int result;
|
|
int remaining = argc;
|
|
char **leftover;
|
|
|
|
int sockfd = -1;
|
|
struct addrinfo hints, hints_numeric, *res;
|
|
int error;
|
|
char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
|
|
#ifdef NI_WITHSCOPEID
|
|
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
|
|
#else
|
|
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
|
|
#endif
|
|
|
|
FILE *files = NULL;
|
|
CHANNEL channel;
|
|
int verbose = FALSE;
|
|
int dump_config = FALSE;
|
|
int module_find = FALSE;
|
|
int n, nr = 0;
|
|
#ifdef OPENSSL
|
|
int sslcode;
|
|
char rand_file[MAX_LINE];
|
|
#endif
|
|
char *sendline, recvline[MAX_LINE + 1];
|
|
boolean accept_http_redirects = FALSE;
|
|
char *http_hostname = NULL;
|
|
#ifdef ICP
|
|
char retcode[DEFLINE];
|
|
int length;
|
|
#endif
|
|
struct timeval newtv, oldtv;
|
|
void printstats();
|
|
|
|
#ifdef HAVE_USLEEP
|
|
float wait = 1.0;
|
|
#else
|
|
unsigned int wait = 1;
|
|
#endif
|
|
unsigned char fill = ' ';
|
|
char *fill_s;
|
|
boolean fill_requested = FALSE;
|
|
unsigned int i = 0;
|
|
char *plugin_name = NULL;
|
|
char *complete_plugin_name = NULL;
|
|
char *ext;
|
|
void *plugin = NULL;
|
|
int plugin_result;
|
|
|
|
void to_alarm(); /* our alarm() signal handler */
|
|
void interrupted();
|
|
unsigned int timeout = 10;
|
|
boolean timeout_requested = 0;
|
|
boolean size_requested = 0;
|
|
char *url = "";
|
|
#if USE_SIGACTION
|
|
struct sigaction mysigaction;
|
|
#endif
|
|
|
|
char *plugin_port_name, *port_name;
|
|
boolean plugin_raw = FALSE;
|
|
boolean port_to_use = USE_ECHO;
|
|
boolean http = 0;
|
|
boolean smtp = 0;
|
|
boolean discard = 0;
|
|
boolean chargen = 0;
|
|
boolean udp = 0;
|
|
boolean icp = 0;
|
|
|
|
boolean nocache = 0;
|
|
|
|
#ifdef ICP
|
|
icp_opcode opcode = ICP_OP_QUERY;
|
|
#endif
|
|
|
|
boolean ttcp = 0;
|
|
boolean tcp = 0;
|
|
boolean ssl = 0;
|
|
|
|
boolean stop_at_newlines = 1;
|
|
|
|
#ifdef OPENSSL
|
|
SSL_METHOD *meth;
|
|
SSL_CTX *ctx = NULL;
|
|
SSL *sslh = NULL;
|
|
#endif
|
|
#ifdef GNUTLS
|
|
gnutls_session session;
|
|
gnutls_certificate_credentials xcred;
|
|
int tls_result;
|
|
const int cert_type_priority[3] = { GNUTLS_CRT_X509,
|
|
GNUTLS_CRT_OPENPGP, 0
|
|
};
|
|
#endif
|
|
|
|
int priority;
|
|
int priority_requested = 0;
|
|
int tos;
|
|
int tos_requested = 0;
|
|
char *p;
|
|
echoping_options global_options;
|
|
|
|
/* popt variables */
|
|
const struct poptOption options[] = {
|
|
{"verbose", 'v', POPT_ARG_NONE, &verbose, 'v'},
|
|
{"dump-configuration", 'V', POPT_ARG_NONE, &dump_config, 'V',
|
|
"Displays echoping compiled-in configuration"},
|
|
{"help", '?', POPT_ARG_NONE, NULL, '?'},
|
|
{"size", 's', POPT_ARG_INT, &size, 's'},
|
|
{"number", 'n', POPT_ARG_INT, &number, 'n', "Number of iterations"},
|
|
#ifdef HAVE_USLEEP
|
|
{"wait", 'w', POPT_ARG_FLOAT, &wait, 'w',
|
|
"Delay between iterations"},
|
|
#else
|
|
{"wait", 'w', POPT_ARG_INT, &wait, 'w', "Delay between iterations"},
|
|
#endif
|
|
{"discard", 'd', POPT_ARG_NONE, &discard, 'd'},
|
|
{"chargen", 'c', POPT_ARG_NONE, &chargen, 'c'},
|
|
{"http", 'h', POPT_ARG_STRING, &url, 'h'},
|
|
{"accept-http-redirects", 'R', POPT_ARG_NONE, &accept_http_redirects,
|
|
'R',
|
|
"Accept HTTP return codes 3xx (redirections)"},
|
|
{"hostname", 'H', POPT_ARG_STRING, &http_hostname, 'H',
|
|
"Hostname to use in HTTP Host: header"},
|
|
{"icp", 'i', POPT_ARG_STRING, &url, 'i',
|
|
"ICP protocol, for Web proxies/caches"},
|
|
{"ttcp", 'r', POPT_ARG_NONE, &ttcp, 'r',
|
|
"Use the T/TCP protocol (Transaction TCP)"},
|
|
{"udp", 'u', POPT_ARG_NONE, &udp, 'u'},
|
|
{"timeout", 't', POPT_ARG_INT, &timeout, 't'},
|
|
{"fill", 'f', POPT_ARG_STRING, &fill_s, 'f'},
|
|
{"smtp", 'S', POPT_ARG_NONE, &smtp, 'S'},
|
|
{"ssl", 'C', POPT_ARG_NONE, &ssl, 'C'},
|
|
{"priority", 'p', POPT_ARG_INT, &priority, 'p'},
|
|
{"type-of-service", 'P', POPT_ARG_INT, &tos, 'P'},
|
|
{"check-original", 'a', POPT_ARG_NONE, NULL, 'a',
|
|
"For HTTP through a proxy/cache"},
|
|
{"ignore-cache", 'A', POPT_ARG_NONE, NULL, 'A',
|
|
"For HTTP through a proxy/cache"},
|
|
{"ipv4", '4', POPT_ARG_NONE, NULL, '4'},
|
|
{"ipv6", '6', POPT_ARG_NONE, NULL, '6'},
|
|
{"module", 'm', POPT_ARG_STRING, &plugin_name, 'm',
|
|
"Loads the given plugin"},
|
|
POPT_TABLEEND
|
|
};
|
|
poptContext poptcon;
|
|
|
|
global_options.udp = FALSE;
|
|
global_options.ttcp = FALSE;
|
|
global_options.verbose = FALSE;
|
|
|
|
null_timeval.tv_sec = 0;
|
|
null_timeval.tv_usec = 0;
|
|
max_timeval.tv_sec = 1000000000;
|
|
max_timeval.tv_usec = 999999;
|
|
|
|
return_code = 0;
|
|
number = 1;
|
|
total = null_timeval;
|
|
median = null_timeval;
|
|
max = null_timeval;
|
|
min = max_timeval;
|
|
stddev = null_timeval;
|
|
port_name = malloc(NI_MAXSERV);
|
|
strcpy(port_name, ECHO_TCP_PORT);
|
|
|
|
for (i = 0; i <= MAX_ITERATIONS; i++) {
|
|
results[i].valid = 0;
|
|
}
|
|
progname = (char *) argv[0];
|
|
|
|
poptcon =
|
|
poptGetContext(NULL, argc, argv, options, POPT_CONTEXT_POSIXMEHARDER);
|
|
|
|
while ((result = poptGetNextOpt(poptcon)) != -1) {
|
|
if (result < -1) {
|
|
fprintf(stderr, "%s: %s\n",
|
|
poptBadOption(poptcon, POPT_BADOPTION_NOALIAS),
|
|
poptStrerror(result));
|
|
usage(poptcon);
|
|
}
|
|
remaining--;
|
|
switch ((char) result) {
|
|
case '?':
|
|
poptPrintHelp(poptcon, stdout, 0);
|
|
fprintf(stdout, " hostname [plugin-options...]\n");
|
|
fprintf(stdout,
|
|
" (You can get a list of available plugins with \"ls %s\")\n",
|
|
PLUGINS_DIR);
|
|
exit(0);
|
|
case 'V':
|
|
printf("%s\n", COMPILATION_OPTIONS);
|
|
exit(0);
|
|
case 'v':
|
|
break;
|
|
case 'r':
|
|
break;
|
|
case 'u':
|
|
break;
|
|
case 'C':
|
|
break;
|
|
case 'd':
|
|
strcpy(port_name, DISCARD_TCP_PORT);
|
|
port_to_use = USE_DISCARD;
|
|
break;
|
|
case 'c':
|
|
strcpy(port_name, CHARACTER_GENERATOR_TCP_PORT);
|
|
port_to_use = USE_CHARGEN;
|
|
stop_at_newlines = 0;
|
|
break;
|
|
case 'i':
|
|
remaining--;
|
|
strcpy(port_name, DEFAULT_ICP_UDP_PORT);
|
|
port_to_use = USE_ICP;
|
|
udp = 1;
|
|
icp = 1;
|
|
break;
|
|
case 'h':
|
|
remaining--;
|
|
strcpy(port_name, DEFAULT_HTTP_TCP_PORT);
|
|
port_to_use = USE_HTTP;
|
|
http = 1;
|
|
break;
|
|
case 'R':
|
|
accept_http_redirects = TRUE;
|
|
break;
|
|
case 'H':
|
|
remaining--;
|
|
break;
|
|
case 'a':
|
|
nocache = 1;
|
|
break;
|
|
case 'A':
|
|
nocache = 2;
|
|
break;
|
|
case 'f':
|
|
remaining--;
|
|
if (strlen(fill_s) > 1)
|
|
err_quit
|
|
("Argument --fill should be a one-character string");
|
|
fill = fill_s[0];
|
|
fill_requested = 1;
|
|
break;
|
|
case 'S':
|
|
strcpy(port_name, "smtp");
|
|
port_to_use = USE_SMTP;
|
|
break;
|
|
case 'p':
|
|
remaining--;
|
|
priority_requested = 1;
|
|
break;
|
|
case 'P':
|
|
remaining--;
|
|
tos_requested = 1;
|
|
break;
|
|
case 's':
|
|
remaining--;
|
|
if (size > MAX_LINE) {
|
|
(void) fprintf(stderr,
|
|
"%s: packet size too large, max is %d.\n",
|
|
progname, MAX_LINE);
|
|
exit(1);
|
|
}
|
|
if (size <= 0) {
|
|
(void) fprintf(stderr, "%s: illegal packet size.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
size_requested = 1;
|
|
break;
|
|
case 't':
|
|
remaining--;
|
|
timeout_requested = 1;
|
|
if (size <= 0) {
|
|
(void) fprintf(stderr, "%s: illegal timeout.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'n':
|
|
remaining--;
|
|
if (number > MAX_ITERATIONS) {
|
|
(void) fprintf(stderr,
|
|
"%s: number of iterations too large, max is %d.\n",
|
|
progname, MAX_ITERATIONS);
|
|
exit(1);
|
|
}
|
|
if (number <= 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: illegal number of iterations.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'w':
|
|
remaining--;
|
|
if (wait <= 0)
|
|
/*
|
|
* atoi returns zero when there is an error.
|
|
* So we cannot use '-w 0' to specify no
|
|
* waiting.
|
|
*/
|
|
{
|
|
(void) fprintf(stderr,
|
|
"%s: illegal waiting time.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case '4':
|
|
family = AF_INET;
|
|
break;
|
|
case '6':
|
|
family = AF_INET6;
|
|
break;
|
|
case 'm':
|
|
remaining--;
|
|
module_find = TRUE;
|
|
break;
|
|
default:
|
|
printf("Unknown character option %d (%c)", result,
|
|
(char) result);
|
|
usage(poptcon);
|
|
}
|
|
}
|
|
if (udp && ((port_to_use == USE_CHARGEN) ||
|
|
(port_to_use == USE_HTTP) || (port_to_use == USE_SMTP))) {
|
|
(void) fprintf(stderr,
|
|
"%s: I don't know how to use this port with UDP.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
if ((http || smtp) && (fill_requested)) {
|
|
(void) fprintf(stderr,
|
|
"%s: Filling incompatible with HTTP connections.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#ifndef USE_TTCP
|
|
if (ttcp) {
|
|
(void) fprintf(stderr,
|
|
"%s: not compiled with T/TCP support.\n", progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#if ! (defined(OPENSSL) || defined(GNUTLS))
|
|
if (ssl) {
|
|
(void) fprintf(stderr,
|
|
"%s: not compiled with SSL/TLS support.\n", progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#ifndef HTTP
|
|
if (http) {
|
|
(void) fprintf(stderr, "%s: Not compiled with HTTP support.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#ifndef SMTP
|
|
if (smtp) {
|
|
(void) fprintf(stderr, "%s: Not compiled with SMTP support.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#ifndef ICP
|
|
if (icp) {
|
|
(void) fprintf(stderr, "%s: Not compiled with ICP support.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
if ((http || smtp) && size_requested) {
|
|
(void) fprintf(stderr,
|
|
"%s: %s and message size specification are incompatible.\n",
|
|
progname, http ? "HTTP" : "SMTP");
|
|
exit(1);
|
|
}
|
|
if (ssl && !http) {
|
|
(void) fprintf(stderr,
|
|
"%s: SSL is only supported for HTTP requests.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
if (udp && ttcp) {
|
|
(void) fprintf(stderr, "%s: UDP and T/TCP are incompatible.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
if (ssl && http) {
|
|
strcpy(port_name, DEFAULT_HTTPS_TCP_PORT);
|
|
}
|
|
if (!http && accept_http_redirects) {
|
|
(void) fprintf(stderr,
|
|
"%s: accept-http-redirects does not make sens if you do not use HTTP.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#ifndef USE_TOS
|
|
if (tos_requested) {
|
|
(void) fprintf(stderr,
|
|
"%s: Not compiled with Type Of Service support.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#ifndef USE_PRIORITY
|
|
if (priority_requested) {
|
|
(void) fprintf(stderr,
|
|
"%s: Not compiled with socket priority support.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
remaining--; /* No argv[0] this time */
|
|
leftover = (char **) &argv[argc - remaining];
|
|
if (plugin_name) {
|
|
ext = strstr(plugin_name, ".so");
|
|
if ((ext == NULL) || (strcmp(ext, ".so") != 0))
|
|
sprintf(plugin_name, "%s.so", plugin_name);
|
|
plugin = dlopen(plugin_name, RTLD_NOW);
|
|
if (!plugin) {
|
|
/* Retries with the absolute name */
|
|
complete_plugin_name = (char *) malloc(MAX_LINE);
|
|
sprintf(complete_plugin_name, "%s/%s", PLUGINS_DIR,
|
|
plugin_name);
|
|
plugin = dlopen(complete_plugin_name, RTLD_NOW);
|
|
}
|
|
if (!plugin) {
|
|
#if DEBIAN
|
|
/* A bit of help for the poor user */
|
|
fprintf(stderr,
|
|
"You may have to load the recommended packages "
|
|
"to run this plugin.\n"
|
|
"'dpkg -s echoping | grep Recommends' to know them.\n\n");
|
|
#endif
|
|
err_sys
|
|
("Cannot load \"%s\" (I tried the short name, then the complete name in \"%s\"): %s",
|
|
plugin_name, PLUGINS_DIR, dlerror());
|
|
}
|
|
plugin_init = dlsym(plugin, "init");
|
|
if (!plugin_init) {
|
|
err_sys("Cannot find init in %s: %s", plugin_name,
|
|
dlerror());
|
|
}
|
|
global_options.udp = udp;
|
|
global_options.ttcp = ttcp;
|
|
global_options.verbose = verbose;
|
|
if (family == AF_INET)
|
|
global_options.only_ipv4 = 1;
|
|
else
|
|
global_options.only_ipv4 = 0;
|
|
if (family == AF_INET6)
|
|
global_options.only_ipv6 = 1;
|
|
else
|
|
global_options.only_ipv6 = 0;
|
|
plugin_port_name =
|
|
plugin_init(remaining, (const char **) leftover, global_options);
|
|
if (plugin_port_name != NULL) {
|
|
strcpy(port_name, plugin_port_name);
|
|
plugin_raw = FALSE;
|
|
plugin_start = dlsym(plugin, "start");
|
|
if (!plugin_start) {
|
|
err_sys("Cannot find start in %s: %s", plugin_name,
|
|
dlerror());
|
|
}
|
|
} else {
|
|
port_name = 0;
|
|
plugin_raw = TRUE;
|
|
plugin_raw_start = dlsym(plugin, "start_raw");
|
|
if (!plugin_raw_start) {
|
|
err_sys("Cannot find start_raw in %s: %s",
|
|
plugin_name, dlerror());
|
|
}
|
|
}
|
|
plugin_execute = dlsym(plugin, "execute");
|
|
if (!plugin_execute) {
|
|
err_sys("Cannot find execute in %s: %s", plugin_name,
|
|
dlerror());
|
|
}
|
|
plugin_terminate = dlsym(plugin, "terminate");
|
|
if (!plugin_terminate) {
|
|
err_sys("Cannot find terminate in %s: %s", plugin_name,
|
|
dlerror());
|
|
}
|
|
}
|
|
if (!udp && !ttcp) {
|
|
tcp = 1;
|
|
}
|
|
if (remaining == 0) {
|
|
(void) fprintf(stderr, "No host name indicated\n");
|
|
usage(poptcon);
|
|
}
|
|
if (!module_find && remaining != 1) {
|
|
printf("%d args remaining, should be 1\n", remaining);
|
|
usage(poptcon);
|
|
}
|
|
if (verbose) {
|
|
printf("\nThis is %s, version %s.\n\n", progname, VERSION);
|
|
}
|
|
server = leftover[0];
|
|
#ifdef LIBIDN
|
|
locale_server = server;
|
|
utf8_server = stringprep_locale_to_utf8(server);
|
|
if (utf8_server)
|
|
server = utf8_server;
|
|
else
|
|
err_quit("Cannot convert %s to UTF-8 encoding: wrong locale (%s)?",
|
|
server, stringprep_locale_charset());
|
|
#endif
|
|
if (!http && !icp) {
|
|
for (p = server; *p && (*p != ':'); p++) {
|
|
}
|
|
if (*p && (*p == ':')) {
|
|
(void) fprintf(stderr,
|
|
"%s: The syntax hostname:port is only for HTTP or ICP\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
}
|
|
signal(SIGINT, interrupted);
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = family;
|
|
hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM;
|
|
|
|
#ifdef HTTP
|
|
if (http || icp) {
|
|
char *text_port = NULL;
|
|
|
|
if (*server == '[') { /* RFC 2732 */
|
|
server++;
|
|
for (p = server; *p && *p != ']'; p++) {
|
|
}
|
|
p++;
|
|
if (*p == ':') {
|
|
p--;
|
|
*p = 0;
|
|
text_port = p + 2;
|
|
strncpy(port_name, text_port, NI_MAXSERV);
|
|
} else {
|
|
p--;
|
|
if (*p == ']') {
|
|
/* No port number */
|
|
*p = 0;
|
|
} else {
|
|
(void) fprintf(stderr,
|
|
"%s: Invalid syntax for IPv6 address.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
}
|
|
} else {
|
|
for (p = server; *p; p++) {
|
|
if (*p == ':') {
|
|
*p = 0;
|
|
text_port = p + 1;
|
|
if (strcmp(text_port, "")) /* See bug *
|
|
* #850672 */
|
|
strncpy(port_name, text_port,
|
|
NI_MAXSERV);
|
|
}
|
|
}
|
|
}
|
|
if (text_port == NULL || !strcmp(text_port, "")) {
|
|
error = getaddrinfo(server, port_name, &hints, &res);
|
|
if (error) {
|
|
if (error == EAI_SERVICE) {
|
|
if (strcmp(port_name, DEFAULT_HTTP_TCP_PORT)
|
|
== 0) {
|
|
strcpy(port_name, "80");
|
|
} else
|
|
if (strcmp
|
|
(port_name, DEFAULT_HTTPS_TCP_PORT)
|
|
== 0) {
|
|
strcpy(port_name, "443");
|
|
} else
|
|
if (strcmp
|
|
(port_name, DEFAULT_ICP_UDP_PORT)
|
|
== 0) {
|
|
strcpy(port_name, "3130");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef LIBIDN
|
|
/*
|
|
* Check if it is an address or a name (libidn will have trouble with
|
|
* IPv6 addresses otherwise)
|
|
*/
|
|
memset(&hints_numeric, 0, sizeof(hints_numeric));
|
|
hints_numeric.ai_family = family;
|
|
hints_numeric.ai_flags = AI_NUMERICHOST;
|
|
error = getaddrinfo(server, port_name, &hints_numeric, &res);
|
|
if (error && error == EAI_NONAME) { /* A name, not an address */
|
|
if ((result =
|
|
idna_to_ascii_8z(utf8_server, &ace_server,
|
|
IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS) {
|
|
if (result == IDNA_CONTAINS_LDH)
|
|
err_quit("Illegal name for host: %s", server); /* foo@bar
|
|
* or
|
|
* similar
|
|
* errors */
|
|
else
|
|
err_quit("IDN error for host: %s %d", server,
|
|
result);
|
|
}
|
|
if (strcmp(utf8_server, ace_server)) {
|
|
if (verbose)
|
|
printf("ACE name of the server: %s\n", ace_server);
|
|
server = ace_server;
|
|
}
|
|
} /* Else it is an address, do not IDNize it */
|
|
#endif
|
|
if (!plugin || !plugin_raw) {
|
|
error = getaddrinfo(server, port_name, &hints, &res);
|
|
if (error) {
|
|
err_quit("getaddrinfo error for host: %s %s",
|
|
server, gai_strerror(error));
|
|
}
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
|
|
pbuf, sizeof(pbuf), niflags) != 0) {
|
|
strcpy(hbuf, "?");
|
|
strcpy(pbuf, "?");
|
|
}
|
|
}
|
|
if (plugin) {
|
|
if (verbose) {
|
|
printf("Running start() for the plugin %s...\n",
|
|
plugin_name);
|
|
}
|
|
if (plugin_raw)
|
|
plugin_raw_start();
|
|
else
|
|
plugin_start(res);
|
|
}
|
|
#ifdef HTTP
|
|
if (http) {
|
|
if (http_hostname != NULL)
|
|
server = http_hostname;
|
|
sendline = make_http_sendline(url, server, atoi(pbuf), nocache);
|
|
} else
|
|
#endif
|
|
#ifdef SMTP
|
|
if (smtp) {
|
|
sendline = "QUIT\r\n"; /* Surprises some SMTP servers which log a
|
|
* frightening NOQUEUE. Anyone knows better?
|
|
*/
|
|
} else
|
|
#endif
|
|
#ifdef ICP
|
|
if (icp) {
|
|
if (res->ai_family == AF_INET) {
|
|
sendline =
|
|
make_icp_sendline(url, &(res->ai_addr), opcode, &length);
|
|
} else {
|
|
|
|
/*
|
|
* TODO: we should be able to create a ICP hostid for
|
|
* IPv6 addresses... See the Squid IPv6 patch at
|
|
* http://devel.squid-cache.org/projects.html#ipv6,
|
|
* for instance the following code.
|
|
*/
|
|
sendline =
|
|
make_icp_sendline(url, (void *) NULL, opcode, &length);
|
|
/*
|
|
* - headerp->shostid = theOutICPAddr.s_addr; + ** FIXME ** we
|
|
* should get more unique data from IPv6 address +xmemcpy
|
|
* (&headerp->shostid, &theOutICPAddr, sizeof
|
|
* (headerp->shostid)); */
|
|
}
|
|
} else
|
|
#endif
|
|
if (!fill_requested) {
|
|
sendline = random_string(size);
|
|
} else {
|
|
sendline = (char *) malloc(size + 1);
|
|
for (i = 0; i < size; i++)
|
|
sendline[i] = fill;
|
|
sendline[size] = 0;
|
|
}
|
|
n = strlen(sendline);
|
|
|
|
#ifdef OPENSSL
|
|
if (ssl) {
|
|
SSL_load_error_strings();
|
|
SSLeay_add_ssl_algorithms();
|
|
/*
|
|
* The following RAND_ calls are only for systems insecure
|
|
* enough to fail to have /dev/urandom. Bug #132001
|
|
*/
|
|
RAND_file_name(rand_file, sizeof(rand_file));
|
|
RAND_write_file(rand_file);
|
|
RAND_load_file(rand_file, 1024);
|
|
meth = SSLv23_client_method();
|
|
if ((ctx = SSL_CTX_new(meth)) == NULL)
|
|
err_sys("Cannot create a new SSL context");
|
|
/*
|
|
* Bug reported by Sebastian Siewior
|
|
* <bigeasy@foo.fh-furtwangen.de>. It seems that OpenSSL
|
|
* crashes on non blocking sockets when trying to close them
|
|
* (OpenSSL will try write on a closed socket).
|
|
*/
|
|
#ifdef USE_SIGACTION
|
|
mysigaction.sa_handler = SIG_IGN;
|
|
sigemptyset(&mysigaction.sa_mask);
|
|
if ((sigaction(SIGPIPE, &mysigaction, NULL)) < 0); /* Ignore it
|
|
*/
|
|
#else
|
|
signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef GNUTLS
|
|
if (ssl) {
|
|
gnutls_global_init();
|
|
gnutls_certificate_allocate_credentials(&xcred);
|
|
/*
|
|
* gnutls_certificate_set_x509_trust_file(xcred, CAFILE,
|
|
* GNUTLS_X509_FMT_PEM);
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
for (i = 1; i <= number; i++) {
|
|
timeout_flag = 0;
|
|
if (timeout_requested)
|
|
alarm(timeout);
|
|
if (i > 1) {
|
|
#ifdef HAVE_USLEEP
|
|
/*
|
|
* SUSv3 states that the argument to usleep() shall
|
|
* be less * than 1000000, so split into two calls if
|
|
* necessary. Bug #1473872, fix by Jeff Rizzo -
|
|
* riz@sourceforge
|
|
*/
|
|
if (wait >= 1) {
|
|
sleep((unsigned int) wait);
|
|
}
|
|
usleep((wait - (unsigned int) wait) * 1000000);
|
|
#else
|
|
sleep(wait);
|
|
#endif
|
|
}
|
|
attempts++;
|
|
#ifdef OPENSSL
|
|
if (ssl)
|
|
/*
|
|
* Despite what the OpenSSL documentation says, we
|
|
* must allocate a new SSL structure at each
|
|
* iteration, otherwise, some* SSL servers fail at
|
|
* the second iteration with: error:1406D0D9:SSL
|
|
* routines:GET_SERVER_HELLO:reuse cert type not zero
|
|
* Bug #130151
|
|
*/
|
|
if ((sslh = SSL_new(ctx)) == NULL)
|
|
err_sys("Cannot initialize SSL context");
|
|
#endif
|
|
/*
|
|
* Open a socket.
|
|
*/
|
|
if (!plugin) {
|
|
if ((sockfd =
|
|
socket(res->ai_family, res->ai_socktype,
|
|
res->ai_protocol)) < 0)
|
|
err_sys("Can't open socket");
|
|
if (udp) {
|
|
struct addrinfo hints2, *res2;
|
|
|
|
memset(&hints2, 0, sizeof(hints2));
|
|
hints2.ai_family = res->ai_family;
|
|
hints2.ai_flags = AI_PASSIVE;
|
|
hints2.ai_socktype = SOCK_DGRAM;
|
|
error = getaddrinfo(NULL, "0", &hints2, &res2);
|
|
if (error) {
|
|
err_sys("getaddrinfo error");
|
|
}
|
|
if (bind(sockfd, res2->ai_addr, res2->ai_addrlen) <
|
|
0) {
|
|
err_sys("bind error");
|
|
}
|
|
}
|
|
#ifdef USE_PRIORITY
|
|
if (priority_requested) {
|
|
if (verbose) {
|
|
printf
|
|
("Setting socket priority to %d (0x%02x)\n",
|
|
priority, (unsigned int) priority);
|
|
}
|
|
if (setsockopt(sockfd,
|
|
SOL_SOCKET,
|
|
SO_PRIORITY,
|
|
(void *) &priority,
|
|
(socklen_t) sizeof(priority))) {
|
|
err_sys("Failed setting socket priority");
|
|
}
|
|
}
|
|
#endif
|
|
#if USE_TOS
|
|
if (tos_requested) {
|
|
if (verbose) {
|
|
printf
|
|
("Setting IP type of service octet to %d (0x%02x)\n",
|
|
tos, (unsigned int) tos);
|
|
}
|
|
if (setsockopt(sockfd,
|
|
SOL_IP,
|
|
IP_TOS, (void *) &tos,
|
|
(socklen_t) sizeof(tos))) {
|
|
err_sys
|
|
("Failed setting IP type of service octet");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (verbose) {
|
|
if (!plugin) {
|
|
if (tcp) {
|
|
printf
|
|
("Trying to connect to internet address %s %s to transmit %u bytes...\n",
|
|
hbuf, pbuf, n);
|
|
}
|
|
#ifdef ICP
|
|
if (icp) {
|
|
printf
|
|
("Trying to send an ICP packet of %u bytes to the internet address %s...\n",
|
|
length, hbuf);
|
|
}
|
|
#endif
|
|
else {
|
|
printf
|
|
("Trying to send %u bytes to internet address %s...\n",
|
|
size, hbuf);
|
|
}
|
|
} else {
|
|
if (plugin_raw)
|
|
printf("Trying to call plugin %s...\n",
|
|
plugin_name);
|
|
else
|
|
printf
|
|
("Trying to call plugin %s for internet address %s %s...\n",
|
|
plugin_name, hbuf, pbuf);
|
|
}
|
|
}
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
if ((tcp || plugin) && timeout_requested) { /* echoping's
|
|
* timeout has a
|
|
* different
|
|
* semantic in TCP
|
|
* and UDP */
|
|
#ifdef USE_SIGACTION
|
|
mysigaction.sa_handler = to_alarm;
|
|
sigemptyset(&mysigaction.sa_mask);
|
|
/* Default behavior doesn't seem portable? */
|
|
#ifdef SA_INTERRUPT
|
|
mysigaction.sa_flags = SA_INTERRUPT;
|
|
#else
|
|
mysigaction.sa_flags = (int) 0;
|
|
#endif
|
|
if ((sigaction(SIGALRM, &mysigaction, NULL)) < 0)
|
|
err_sys("Cannot set signal handler");
|
|
#else
|
|
signal(SIGALRM, to_alarm);
|
|
#endif
|
|
timeout_flag = 0; /* for signal handler */
|
|
alarm(timeout);
|
|
}
|
|
(void) gettimeofday(&oldtv, (struct timezone *) NULL);
|
|
if (plugin) {
|
|
plugin_result = plugin_execute();
|
|
if (plugin_result == -2)
|
|
err_quit("");
|
|
} else {
|
|
if (!ttcp && !icp) {
|
|
/*
|
|
* Connect to the server.
|
|
*/
|
|
(void) gettimeofday(&conntv,
|
|
(struct timezone *) NULL);
|
|
if (connect(sockfd, res->ai_addr, res->ai_addrlen) <
|
|
0) {
|
|
if ((errno == EINTR) && (timeout_flag)) {
|
|
printf("Timeout while connecting\n");
|
|
close(sockfd);
|
|
continue;
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
} else
|
|
err_sys("Can't connect to server");
|
|
} else {
|
|
if (tcp) {
|
|
(void) gettimeofday(&connectedtv,
|
|
(struct timezone
|
|
*)
|
|
NULL);
|
|
temp = connectedtv;
|
|
tvsub(&temp, &conntv);
|
|
if (verbose) {
|
|
printf("Connected...\n");
|
|
printf
|
|
("TCP Latency: %d.%06d seconds\n",
|
|
(int) temp.tv_sec,
|
|
(int) temp.tv_usec);
|
|
}
|
|
}
|
|
}
|
|
if (verbose && tcp) {
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
}
|
|
if (!udp && !ssl)
|
|
if ((files = fdopen(sockfd, "r")) == NULL)
|
|
err_sys("Cannot fdopen");
|
|
#ifdef OPENSSL
|
|
if (ssl) {
|
|
SSL_set_fd(sslh, sockfd);
|
|
if (SSL_connect(sslh) == -1)
|
|
if ((errno == EINTR)
|
|
&& (timeout_flag)) {
|
|
printf
|
|
("Timeout while starting SSL\n");
|
|
close(sockfd);
|
|
continue;
|
|
}
|
|
if (verbose)
|
|
printf("SSL connection using %s\n",
|
|
SSL_get_cipher(sslh));
|
|
/*
|
|
* We could check the server's
|
|
* certificate or other funny things
|
|
*/
|
|
}
|
|
#endif
|
|
#ifdef GNUTLS
|
|
if (ssl) {
|
|
tls_result =
|
|
gnutls_init(&session, GNUTLS_CLIENT);
|
|
if (tls_result != 0)
|
|
err_sys
|
|
("Cannot create a new TLS session");
|
|
gnutls_set_default_priority(session);
|
|
gnutls_certificate_type_set_priority(session,
|
|
cert_type_priority);
|
|
gnutls_credentials_set(session,
|
|
GNUTLS_CRD_CERTIFICATE,
|
|
xcred);
|
|
gnutls_transport_set_ptr(session,
|
|
(gnutls_transport_ptr)
|
|
sockfd);
|
|
tls_result = gnutls_handshake(session);
|
|
if (tls_result < 0) {
|
|
if ((errno == EINTR)
|
|
&& (timeout_flag)) {
|
|
printf
|
|
("Timeout while starting TLS\n");
|
|
close(sockfd);
|
|
continue;
|
|
} else {
|
|
err_sys
|
|
("Cannot start the TLS session: %s",
|
|
gnutls_strerror
|
|
(tls_result));
|
|
}
|
|
}
|
|
if (verbose)
|
|
printf
|
|
("TLS connection using \"%s\"\n",
|
|
gnutls_cipher_get_name
|
|
(gnutls_cipher_get(session)));
|
|
/*
|
|
* We could check the server's
|
|
* certificate or other funny things.
|
|
* See
|
|
* http://www.gnu.org/software/gnutls/
|
|
* documentation/gnutls/gnutls.html#SE
|
|
* CTION00622000000000000000
|
|
*/
|
|
}
|
|
#endif
|
|
}
|
|
/* Not T/TCP */
|
|
else {
|
|
/* No initial connection */
|
|
}
|
|
if ((port_to_use == USE_ECHO) || (port_to_use == USE_DISCARD)
|
|
|| (port_to_use == USE_HTTP) || (port_to_use == USE_ICP)
|
|
|| (port_to_use == USE_SMTP)) {
|
|
#ifdef USE_TTCP
|
|
if (ttcp) {
|
|
if (sendto(sockfd, sendline, n, MSG_EOF,
|
|
res->ai_addr,
|
|
res->ai_addrlen) != n)
|
|
err_sys("sendto error on socket");
|
|
if (verbose) {
|
|
printf("T/TCP connection done\n");
|
|
}
|
|
} else
|
|
#endif
|
|
if (!udp) {
|
|
if (!ssl) {
|
|
/*
|
|
* Write something to the
|
|
* server
|
|
*/
|
|
if (writen(sockfd, sendline, n) != n) {
|
|
if ((nr < 0 || nr != n)
|
|
&& timeout_flag) {
|
|
printf
|
|
("Timeout while writing (%d byte(s) written so far)\n",
|
|
(nr ==
|
|
-1) ? 0 : nr);
|
|
nr = n;
|
|
close(sockfd);
|
|
continue;
|
|
} else
|
|
err_sys
|
|
("writen error on TCP socket %d",
|
|
sockfd);
|
|
}
|
|
}
|
|
#ifdef OPENSSL
|
|
else {
|
|
if ((rc =
|
|
SSL_write(sslh, sendline,
|
|
n)) != n) {
|
|
if ((nr < 0 || nr != n)
|
|
&& timeout_flag) {
|
|
nr = n;
|
|
printf
|
|
("Timeout while writing\n");
|
|
close(sockfd);
|
|
continue;
|
|
} else {
|
|
sslcode =
|
|
ERR_get_error();
|
|
err_sys
|
|
("SSL_write error on socket: %s",
|
|
ERR_error_string
|
|
(sslcode,
|
|
NULL));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef GNUTLS
|
|
else
|
|
{
|
|
if ((rc =
|
|
gnutls_record_send(session,
|
|
sendline,
|
|
strlen
|
|
(sendline)))
|
|
!= n) {
|
|
if ((nr < 0 || nr != n)
|
|
&& timeout_flag) {
|
|
nr = n;
|
|
printf
|
|
("Timeout while writing\n");
|
|
close(sockfd);
|
|
continue;
|
|
} else {
|
|
err_sys
|
|
("gnutls_record_send error %d on socket: %s",
|
|
rc,
|
|
gnutls_strerror
|
|
(rc));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef ICP
|
|
if (icp) {
|
|
if (sendto
|
|
(sockfd, sendline, length, 0,
|
|
res->ai_addr,
|
|
res->ai_addrlen) != length)
|
|
err_sys
|
|
("sendto error on socket");
|
|
} else
|
|
#endif
|
|
/*
|
|
* if (sendto(sockfd, sendline, n, 0,
|
|
* &serv_addr, sizeof(serv_addr)) != n)
|
|
* err_sys("sendto error on socket");
|
|
*/
|
|
if (send(sockfd, sendline, n, 0) != n)
|
|
err_sys("send error on socket");
|
|
}
|
|
if (verbose) {
|
|
(void) gettimeofday(&sendtv,
|
|
(struct timezone *)
|
|
NULL);
|
|
#ifdef ICP
|
|
if (icp)
|
|
printf("Sent (%d bytes)...\n",
|
|
length);
|
|
else
|
|
#endif
|
|
printf("Sent (%d bytes)...\n", n);
|
|
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if (tcp && !discard) {
|
|
fd_set mask;
|
|
int n = 0;
|
|
|
|
FD_ZERO(&mask);
|
|
|
|
if (!(http && ssl))
|
|
n = fileno(files);
|
|
#ifdef OPENSSL
|
|
else {
|
|
n = SSL_get_fd(sslh);
|
|
}
|
|
#endif
|
|
#ifdef GNUTLS
|
|
else
|
|
{
|
|
n = sockfd;
|
|
}
|
|
#endif
|
|
FD_SET(n, &mask);
|
|
if (select(n + 1, &mask, 0, 0, NULL) > 0) {
|
|
(void) gettimeofday(&recvtv,
|
|
(struct timezone *)
|
|
NULL);
|
|
temp = recvtv;
|
|
tvsub(&temp, &sendtv);
|
|
if (verbose)
|
|
printf
|
|
("Application Latency: %d.%06d seconds\n",
|
|
(int) temp.tv_sec,
|
|
(int) temp.tv_usec);
|
|
}
|
|
}
|
|
if ((port_to_use == USE_ECHO) || (port_to_use == USE_CHARGEN)
|
|
|| (port_to_use == USE_HTTP) || (port_to_use == USE_ICP)
|
|
|| (port_to_use == USE_SMTP)) {
|
|
if (!udp) {
|
|
if (!http && !smtp && !discard) {
|
|
/* Read from the server */
|
|
nr = readline(files, recvline, n,
|
|
stop_at_newlines);
|
|
} else if (discard) {
|
|
/* No reply, no read */
|
|
}
|
|
#ifdef HTTP
|
|
else if (http) {
|
|
if (!ssl)
|
|
channel.fs = files;
|
|
#ifdef OPENSSL
|
|
else
|
|
channel.ssl = sslh;
|
|
#endif
|
|
#ifdef GNUTLS
|
|
else
|
|
channel.tls = session;
|
|
#endif
|
|
nr = read_from_server(channel, ssl,
|
|
accept_http_redirects);
|
|
}
|
|
#endif
|
|
#ifdef SMTP
|
|
else if (smtp) {
|
|
nr = smtp_read_response_from_server
|
|
(files);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
#ifdef USE_SIGACTION
|
|
mysigaction.sa_handler = to_alarm;
|
|
sigemptyset(&mysigaction.sa_mask);
|
|
#ifdef SA_INTERRUPT
|
|
mysigaction.sa_flags = SA_INTERRUPT;
|
|
#else
|
|
mysigaction.sa_flags = (int) 0;
|
|
#endif
|
|
if ((sigaction(SIGALRM, &mysigaction, NULL))
|
|
< 0)
|
|
err_sys("Cannot set signal handler");
|
|
#else
|
|
signal(SIGALRM, to_alarm);
|
|
#endif
|
|
timeout_flag = 0; /* for signal
|
|
* handler */
|
|
#ifdef ICP
|
|
if (icp) {
|
|
nr = recv_icp(sockfd, recvline,
|
|
retcode);
|
|
if (verbose) {
|
|
printf("%s\n", retcode);
|
|
}
|
|
} else {
|
|
#endif
|
|
nr = recv(sockfd, recvline, n, 0);
|
|
/*
|
|
* nr = recvfrom(sockfd, recvline, n, 0,
|
|
* (struct sockaddr *) 0, (int *) 0);
|
|
* recvfrom fails on SunOS on connected
|
|
* sockets.
|
|
*/
|
|
/*
|
|
* Todo: in UDP, we should loop to read: we
|
|
* can have several reads necessary.
|
|
*/
|
|
if ((nr < 0) && (errno == EINTR)
|
|
&& (timeout_flag)) {
|
|
nr = n;
|
|
printf("Timeout\n");
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) !=
|
|
0) {
|
|
err_sys
|
|
("I cannot flush");
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef ICP
|
|
}
|
|
#endif
|
|
}
|
|
if (!http && !icp && !smtp && !discard) {
|
|
if ((nr < 0 || nr != n) && timeout_flag)
|
|
/*
|
|
* if ((nr < 0 || nr != n) &&
|
|
* (errno == EINTR) &&
|
|
* timeout_flag)
|
|
*/
|
|
{
|
|
printf
|
|
("Timeout while reading (%d byte(s) read)\n",
|
|
(nr == -1) ? 0 : nr);
|
|
nr = n;
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
close(sockfd);
|
|
continue;
|
|
}
|
|
if (nr < 0 || nr != n)
|
|
err_sys
|
|
("readline error: %d bytes read, %d bytes requested",
|
|
nr, n);
|
|
} else
|
|
/* This is probably HTTP */
|
|
{
|
|
/*
|
|
* printf ("DEBUG: received %d bytes
|
|
* (flag is %d, errno is %d)\n", nr,
|
|
* timeout_flag, errno);
|
|
*/
|
|
if ((errno == EINTR) && timeout_flag) {
|
|
printf
|
|
("Timeout while reading (%d byte(s) read so far)\n",
|
|
(nr == -1) ? 0 : nr);
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
close(sockfd);
|
|
continue;
|
|
}
|
|
if (nr < 0) {
|
|
err_ret("Error reading HTTP reply");
|
|
}
|
|
}
|
|
if (verbose)
|
|
printf("%d bytes read from server.\n", nr);
|
|
}
|
|
} /* That's all, folks */
|
|
alarm(0);
|
|
if (http) {
|
|
#ifdef OPENSSL
|
|
if (ssl)
|
|
SSL_shutdown(channel.ssl);
|
|
else
|
|
#endif
|
|
#ifdef GNUTLS
|
|
if (ssl)
|
|
shutdown(sockfd, SHUT_RDWR);
|
|
else
|
|
#endif
|
|
fclose(channel.fs);
|
|
}
|
|
close(sockfd);
|
|
|
|
(void) gettimeofday(&newtv, (struct timezone *) NULL);
|
|
temp = newtv;
|
|
tvsub(&temp, &oldtv);
|
|
if (!timeout_flag) {
|
|
tvadd(&total, &temp);
|
|
|
|
/* Check */
|
|
if (!plugin) {
|
|
if (port_to_use == USE_ECHO) {
|
|
if (strcmp(sendline, recvline) != 0) {
|
|
printf(" I wrote:\n%s\n", sendline);
|
|
printf(" and I got back:\n%s\n",
|
|
recvline);
|
|
err_quit("Strange server");
|
|
}
|
|
if (verbose) {
|
|
printf("Checked\n");
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if (port_to_use == USE_CHARGEN) {
|
|
sendline = CHARGENERATED;
|
|
recvline[strlen(sendline)] = 0;
|
|
if (strcmp(sendline, recvline) != 0) {
|
|
/*
|
|
* TODO: it does not work if
|
|
* the size is lower than the
|
|
* length of CHARGENERATED
|
|
*/
|
|
printf(" I got back:\n%s\n",
|
|
recvline);
|
|
printf
|
|
(" instead of the most common:\n%s\n",
|
|
sendline);
|
|
err_ret("Strange server");
|
|
}
|
|
if (verbose) {
|
|
printf("Checked\n");
|
|
}
|
|
}
|
|
}
|
|
tvsub(&newtv, &oldtv);
|
|
tvmin(&min, &newtv);
|
|
tvmax(&max, &newtv);
|
|
printf("Elapsed time: %d.%06d seconds\n",
|
|
(int) newtv.tv_sec, (int) newtv.tv_usec);
|
|
#ifdef FLUSH_OUTPUT
|
|
if (fflush((FILE *) NULL) != 0) {
|
|
err_sys("I cannot flush");
|
|
}
|
|
#endif
|
|
results[i - 1].valid = 1;
|
|
results[i - 1].timevalue = newtv;
|
|
successes++;
|
|
}
|
|
if (number > 1) {
|
|
#ifdef OPENSSL
|
|
if (ssl) {
|
|
/*
|
|
* SSL_clear (sslh); No, we have to free. Bug
|
|
* #130151
|
|
*/
|
|
SSL_free(sslh);
|
|
}
|
|
#endif
|
|
#ifdef GNUTLS
|
|
if (ssl) {
|
|
gnutls_bye(channel.tls, GNUTLS_SHUT_RDWR);
|
|
gnutls_deinit(session);
|
|
/*
|
|
* gnutls_certificate_free_credentials(xcred);
|
|
*
|
|
*/
|
|
}
|
|
#endif
|
|
}
|
|
} /* End of main loop */
|
|
|
|
/* Clean */
|
|
if (plugin)
|
|
plugin_terminate();
|
|
/* It would be nice to clean here for OpenSSL */
|
|
#ifdef GNUTLS
|
|
if (ssl) {
|
|
gnutls_global_deinit();
|
|
}
|
|
#endif
|
|
printstats();
|
|
if (successes >= 1)
|
|
exit(0);
|
|
else
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
printstats()
|
|
{
|
|
|
|
int i;
|
|
|
|
/* if ((number > 1) && ((!udp) || (successes > 0))) { */
|
|
if (successes > 1) {
|
|
printf("---\n");
|
|
if (successes < attempts)
|
|
printf("Warning: %d message(s) lost (%d %%)\n",
|
|
attempts - successes,
|
|
((attempts - successes) * 100) / attempts);
|
|
printf("Minimum time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
|
(int) min.tv_sec, (int) min.tv_usec,
|
|
(double) size / tv2double(min));
|
|
printf("Maximum time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
|
(int) max.tv_sec, (int) max.tv_usec,
|
|
(double) size / tv2double(max));
|
|
tvavg(&total, successes);
|
|
printf("Average time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
|
(int) total.tv_sec, (int) total.tv_usec,
|
|
(double) size / tv2double(total));
|
|
/*
|
|
* The number of bytes/second, as printed above, is not
|
|
* really meaningful: size does not reflect the number of
|
|
* bytes exchanged. With echo, N = 2*size, with discard, N =
|
|
* size, with http, N = size + (response)...
|
|
*/
|
|
tvstddev(&stddev, successes, total, results);
|
|
printf("Standard deviation: %d.%06d\n",
|
|
(int) stddev.tv_sec, (int) stddev.tv_usec);
|
|
for (i = 0; i < number; i++) {
|
|
if (results[i].valid)
|
|
good_results[j++] = results[i].timevalue;
|
|
}
|
|
if (successes != j) /* Todo: bug! */
|
|
err_quit("successes (%d) is different from j (%d)",
|
|
successes, j);
|
|
qsort(good_results, successes, sizeof(struct timeval), tvcmp);
|
|
/*
|
|
* for (i = 1; i <= number; i++) { printf("---\nTime %d th:
|
|
* %d.%06d seconds\n", i, results[i-1].tv_sec,
|
|
* results[i-1].tv_usec); }
|
|
*/
|
|
if ((successes % 2) == 1) {
|
|
/*
|
|
* printf("Searching good_results[%d]\n", (successes
|
|
* + 1) / 2 - 1);
|
|
*/
|
|
median = good_results[((successes + 1) / 2 - 1)];
|
|
} else {
|
|
/*
|
|
* printf("Searching good_results[%d] and [%d]\n",
|
|
* (successes / 2) - 1, successes / 2);
|
|
*/
|
|
tvadd(&median, &good_results[(successes / 2) - 1]);
|
|
tvadd(&median, &good_results[successes / 2]);
|
|
tvavg(&median, 2);
|
|
}
|
|
printf("Median time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
|
(int) median.tv_sec, (int) median.tv_usec,
|
|
(double) size / tv2double(median));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Signal handler for timeouts (SIGALRM). This function is called when the
|
|
* alarm() value that was set counts down to zero. This indicates that we
|
|
* haven't received a response from the server to the last datagram we sent.
|
|
* All we do is set a flag and return from the signal handler. The occurrence
|
|
* of the signal interrupts the recvfrom() system call (errno = EINTR) above,
|
|
* and we then check the timeout_flag flag.
|
|
*/
|
|
|
|
void
|
|
to_alarm()
|
|
{
|
|
/* printf ("DEBUG: timeout handler called\n"); */
|
|
timeout_flag = 1; /* set flag for function above */
|
|
}
|
|
|
|
void
|
|
interrupted()
|
|
{
|
|
printf("Interrupted by user\n");
|
|
printstats();
|
|
exit(1);
|
|
}
|