echoping/SRC/echoping.c
Stephane Bortzmeyer 80fadd0066 Fixes #850672
2004-07-27 14:05:05 +00:00

1554 lines
36 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];
#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, *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'},
{"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 '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",
http ? "HTTP" : "SMTP", progname);
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);
}
#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)
{
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)
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)
{
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");
}
#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++)
{
if (i > 1)
{
#ifdef HAVE_USLEEP
usleep (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#SECTION00622000000000000000 */
}
#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)
{
nr = n;
printf ("Timeout while writing\n");
close (sockfd);
continue;
}
else
err_sys ("writen error on socket");
}
}
#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
/* Write something to the server */
if (writen (sockfd, sendline, n) != n)
{
if ((nr < 0 || nr != n) && timeout_flag)
{
nr = n;
printf ("Timeout while writing\n");
close (sockfd);
continue;
}
else
err_sys ("writen error on socket");
}
}
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);
}
#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 */
alarm (timeout);
#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.
*/
alarm (0);
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 */
{
if ((nr < 0) && (errno == EINTR) && (timeout_flag))
{
printf ("Timeout while reading (%d byte(s) read)\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 */
if (tcp)
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 ()
{
timeout_flag = 1; /* set flag for function above */
}
void
interrupted ()
{
printf ("Interrupted by user\n");
printstats ();
exit (1);
}