mirror of https://framagit.org/bortzmeyer/echoping
Initial revision
parent
e72827f6b6
commit
40d676b534
@ -0,0 +1,22 @@
|
||||
echoping now uses a numbering scheme analog to the
|
||||
Linux kernel one's. Even versions like 2.2 are stable (or supposed to),
|
||||
odd versions like 2.1 are unstable, beta.
|
||||
|
||||
2.2.1 :
|
||||
Portability fixes
|
||||
|
||||
2.2 :
|
||||
Stable release, apart from ICP
|
||||
|
||||
2.0.1 -> 2.1.0 :
|
||||
Timeouts even on TCP connections.
|
||||
Support of HTTP 1.1
|
||||
|
||||
2.0.0 -> 2.0.1 :
|
||||
|
||||
Bug with getservbyname for HTTP connections fixed.
|
||||
|
||||
1.3 -> 2.0.0 :
|
||||
HTTP support. For testing of Web servers and Web proxies.
|
||||
Much better portability.
|
||||
|
@ -0,0 +1,10 @@
|
||||
Richard Stevens, author of the book "Unix network programming"
|
||||
for his book and his many examples (a lot of the code comes from
|
||||
him by copy-and-paste).
|
||||
|
||||
Pierre Beyssac, author of the "bing" tool for examples of code
|
||||
and beta-testing.
|
||||
|
||||
Christian Grimm for the ICP code.
|
||||
|
||||
|
@ -0,0 +1,98 @@
|
||||
Some details about echoping
|
||||
------------
|
||||
|
||||
|
||||
echo service:
|
||||
|
||||
echoping assumes the remote host accepts such connections. Experience show that
|
||||
most Internet routers do and many hosts also. However, some Unices are not
|
||||
shipped with this service enabled and, anyway, the administrator is always
|
||||
free to close it (I think they shouldn't). echoping has therefore less chance
|
||||
to succeed than ping or bing. (On a typical Unix box, "echo" service is
|
||||
configured in /etc/inetd.conf but see the CERT advisory
|
||||
<http://www.cert.org/advisories/CA-96.01.UDP_service_denial.html>.)
|
||||
|
||||
What does it measure?
|
||||
|
||||
echoping simply shows the elapsed time, including the time to set up the TCP
|
||||
connection and to transfer the data (but excluding the time for the
|
||||
- possible - DNS call). Therefore, it is unsuitable to physical
|
||||
line raw throughput measures (unlike bing). On the other end, the action it
|
||||
performs are close from a HTTP request and it is meaningful to use it
|
||||
(carefully) to measure Web performances.
|
||||
|
||||
UDP and inetd:
|
||||
|
||||
With UDP servers you can have surprises: the first test is quite often
|
||||
much slower since inetd has to launch the process. After that, the process
|
||||
stays a while so the next texts run faster.
|
||||
|
||||
A nice example:
|
||||
|
||||
There are many, many traps when measuring something on the Internet. Just one
|
||||
example: 'echoping -w 0 -n 4 a-sunOS-machine' and you'll see the first test
|
||||
succeed in a very short time (if you are close from the machine) and all of
|
||||
the others take a much longer time (one second). With '-w 1' (wait one second
|
||||
between tests, the default), everything works fine: it seems the sockets on
|
||||
SunOS need time to recover :-)
|
||||
|
||||
A graphical interface:
|
||||
|
||||
If you have the Perl/Tk <http://pubweb.bnl.gov/~ptk/> package, you can use a
|
||||
(quite rough) windowing interface, "echoping.ptk". To use it, you should
|
||||
define FLUSH_OUTPUT at the beginning of echoping.c (this seems to work
|
||||
on only a few Unices, including DEC's OSF/1). This interface has not yet
|
||||
been updated for echoping 2's new features (like HTTP support).
|
||||
|
||||
To measure performances on the Internet you can also see:
|
||||
|
||||
Unix:
|
||||
|
||||
- bing, a bandwidth measurement tool <ftp://ftp.lip6.fr/pub/networking>
|
||||
- ping, probably available with your system
|
||||
- traceroute, idem (otherwise, see <ftp://ftp.ee.lbl.gov/>)
|
||||
- ttcp, the best measurement tool but it needs some control over the
|
||||
two machines <ftp://ftp.arl.mil/pub/ttcp> (nothing to do with
|
||||
the T/TCP protocol)
|
||||
- treno (evaluates available bandwidth for TCP)
|
||||
<http://www.psc.edu/~pscnoc/treno_info.html>
|
||||
- spray is a tool which I dont't know very well. It is available on some
|
||||
machines (Sun, OSF/1).
|
||||
I've also heard of but never tried:
|
||||
- NetPerf <http://www.cup.hp.com/netperf/NetperfPage.html>
|
||||
- a suite of Bandwidth Measuring programs from gnn@netcom.com
|
||||
<ftp://ftp.netcom.com/~ftp/gnn/bwmeas-0.3.tar.Z>. These are several
|
||||
programs that measure bandwidth and jitter over several kinds of
|
||||
IPC links, including TCP and UDP.
|
||||
|
||||
Macintosh:
|
||||
|
||||
- TCP Watcher, a very nice "swiss-army knife" tool, to test ping, DNS, echo.
|
||||
It includes an echo server. Available on Info-Mac in "comm/tcp".
|
||||
|
||||
MS-Windows:
|
||||
|
||||
(I have little knowledge of that environment and I tested nothing.)
|
||||
|
||||
- WSNUTIL. Seems to be an echo client and server.
|
||||
<http://www.ccs.org/winsock/xref-e.html#echo_clients>
|
||||
|
||||
Windows-NT :
|
||||
|
||||
echo and other services can (apparently) be provided within
|
||||
'Simple TCP/IP Services' which
|
||||
can be enabled through the Network Control Panel
|
||||
|
||||
Web clients:
|
||||
|
||||
- You can ping or traceroute on the Web. See
|
||||
<http://hplyot.obspm.fr/cgi-bin/nph-traceroute> or
|
||||
<http://www.fr.net/internet/>.
|
||||
|
||||
|
||||
Use all of them with care, the result is not obvious to interpret.
|
||||
|
||||
And don't forget to read RFC 1470 ("Tools for Monitoring and Debugging
|
||||
TCP/IP Internets and Interconnected Devices"), specially its "Benchmark"
|
||||
section and the Richard Stevens' books (all of them), published by
|
||||
Addison-Wesley.
|
@ -0,0 +1,719 @@
|
||||
/* Parse HyperText Document Address HTParse.c
|
||||
** ================================
|
||||
*/
|
||||
|
||||
#include "HTParse.h"
|
||||
#define TRACE 0
|
||||
|
||||
#define FREE(x) if (x) {free(x); x = NULL;}
|
||||
|
||||
struct struct_parts
|
||||
{
|
||||
char *access;
|
||||
char *host;
|
||||
char *absolute;
|
||||
char *relative;
|
||||
/* char * search; no - treated as part of path */
|
||||
char *anchor;
|
||||
};
|
||||
|
||||
/* Strings of any length
|
||||
** ---------------------
|
||||
*/
|
||||
PUBLIC int strcasecomp
|
||||
ARGS2 (
|
||||
CONST char *, a,
|
||||
CONST char *, b)
|
||||
{
|
||||
CONST char *p = a;
|
||||
CONST char *q = b;
|
||||
|
||||
for (p = a, q = b; *p && *q; p++, q++)
|
||||
{
|
||||
int diff = TOLOWER (*p) - TOLOWER (*q);
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
if (*p)
|
||||
return 1; /* p was longer than q */
|
||||
if (*q)
|
||||
return -1; /* p was shorter than q */
|
||||
return 0; /* Exact match */
|
||||
}
|
||||
|
||||
/* With count limit
|
||||
** ----------------
|
||||
*/
|
||||
PUBLIC int strncasecomp
|
||||
ARGS3 (
|
||||
CONST char *, a,
|
||||
CONST char *, b,
|
||||
int, n)
|
||||
{
|
||||
CONST char *p = a;
|
||||
CONST char *q = b;
|
||||
|
||||
for (p = a, q = b;;
|
||||
p++, q++)
|
||||
{
|
||||
int diff;
|
||||
if (p == (a + n))
|
||||
return 0; /* Match up to n characters */
|
||||
if (!(*p && *q))
|
||||
return (*p - *q);
|
||||
diff = TOLOWER (*p) - TOLOWER (*q);
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
/*NOTREACHED */
|
||||
}
|
||||
|
||||
/* Allocate a new copy of a string, and returns it
|
||||
*/
|
||||
PUBLIC char *HTSACopy
|
||||
ARGS2 (
|
||||
char **, dest,
|
||||
CONST char *, src)
|
||||
{
|
||||
FREE (*dest);
|
||||
if (src)
|
||||
{
|
||||
*dest = (char *) malloc (strlen (src) + 1);
|
||||
if (*dest == NULL)
|
||||
outofmem (__FILE__, "HTSACopy");
|
||||
strcpy (*dest, src);
|
||||
}
|
||||
return *dest;
|
||||
}
|
||||
/* String Allocate and Concatenate
|
||||
*/
|
||||
PUBLIC char *HTSACat
|
||||
ARGS2 (
|
||||
char **, dest,
|
||||
CONST char *, src)
|
||||
{
|
||||
if (src && *src)
|
||||
{
|
||||
if (*dest)
|
||||
{
|
||||
int length = strlen (*dest);
|
||||
*dest = (char *) realloc (*dest, length + strlen (src) + 1);
|
||||
if (*dest == NULL)
|
||||
outofmem (__FILE__, "HTSACat");
|
||||
strcpy (*dest + length, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest = (char *) malloc (strlen (src) + 1);
|
||||
if (*dest == NULL)
|
||||
outofmem (__FILE__, "HTSACat");
|
||||
strcpy (*dest, src);
|
||||
}
|
||||
}
|
||||
return *dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Strip white space off a string. HTStrip()
|
||||
** -------------------------------
|
||||
**
|
||||
** On exit,
|
||||
** Return value points to first non-white character, or to 0 if none.
|
||||
** All trailing white space is OVERWRITTEN with zero.
|
||||
*/
|
||||
PUBLIC char *HTStrip
|
||||
ARGS1 (
|
||||
char *, s)
|
||||
{
|
||||
#define SPACE(c) ((c == ' ') || (c == '\t') || (c == '\n'))
|
||||
char *p = s;
|
||||
for (p = s; *p; p++)
|
||||
; /* Find end of string */
|
||||
for (p--; p >= s; p--)
|
||||
{
|
||||
if (SPACE (*p))
|
||||
*p = '\0'; /* Zap trailing blanks */
|
||||
else
|
||||
break;
|
||||
}
|
||||
while (SPACE (*s))
|
||||
s++; /* Strip leading blanks */
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Scan a filename for its consituents. scan()
|
||||
** ------------------------------------
|
||||
**
|
||||
** On entry,
|
||||
** name points to a document name which may be incomplete.
|
||||
** On exit,
|
||||
** absolute or relative may be nonzero (but not both).
|
||||
** host, anchor and access may be nonzero if they were specified.
|
||||
** Any which are nonzero point to zero terminated strings.
|
||||
*/
|
||||
PRIVATE void scan
|
||||
ARGS2 (
|
||||
char *, name,
|
||||
struct struct_parts *, parts)
|
||||
{
|
||||
char *after_access;
|
||||
char *p;
|
||||
/* int length = strlen (name); */
|
||||
|
||||
parts->access = NULL;
|
||||
parts->host = NULL;
|
||||
parts->absolute = NULL;
|
||||
parts->relative = NULL;
|
||||
parts->anchor = NULL;
|
||||
|
||||
/*
|
||||
** Scan left-to-right for a scheme (access).
|
||||
*/
|
||||
after_access = name;
|
||||
for (p = name; *p; p++)
|
||||
{
|
||||
if (*p == ':')
|
||||
{
|
||||
*p = '\0';
|
||||
parts->access = name; /* Access name has been specified */
|
||||
after_access = (p + 1);
|
||||
break;
|
||||
}
|
||||
if (*p == '/' || *p == '#' || *p == ';' || *p == '?')
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef NOTDEFINED
|
||||
for (p = (name + length - 1); p >= name; p--)
|
||||
{
|
||||
#endif /* NOTDEFINED */
|
||||
/*
|
||||
** Scan left-to-right for a fragment (anchor).
|
||||
*/
|
||||
for (p = after_access; *p; p++)
|
||||
{
|
||||
if (*p == '#')
|
||||
{
|
||||
parts->anchor = (p + 1);
|
||||
*p = '\0'; /* terminate the rest */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Scan left-to-right for a host or absolute path.
|
||||
*/
|
||||
p = after_access;
|
||||
if (*p == '/')
|
||||
{
|
||||
if (p[1] == '/')
|
||||
{
|
||||
parts->host = (p + 2); /* host has been specified */
|
||||
*p = '\0'; /* Terminate access */
|
||||
p = strchr (parts->host, '/'); /* look for end of host name if any */
|
||||
if (p != NULL)
|
||||
{
|
||||
*p = '\0'; /* Terminate host */
|
||||
parts->absolute = (p + 1); /* Root has been found */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parts->absolute = (p + 1); /* Root found but no host */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parts->relative = (*after_access) ?
|
||||
after_access : NULL; /* NULL for "" */
|
||||
}
|
||||
|
||||
/*
|
||||
** Check schemes that commonly have unescaped hashes.
|
||||
*/
|
||||
if (parts->access && parts->anchor)
|
||||
{
|
||||
if ((!parts->host && strcasecomp (parts->access, "lynxcgi")) ||
|
||||
!strcasecomp (parts->access, "nntp") ||
|
||||
!strcasecomp (parts->access, "snews") ||
|
||||
!strcasecomp (parts->access, "news") ||
|
||||
!strcasecomp (parts->access, "data"))
|
||||
{
|
||||
/*
|
||||
* Access specified but no host and not a lynxcgi URL, so the
|
||||
* anchor may not really be one, e.g., news:j462#36487@foo.bar,
|
||||
* or it's an nntp or snews URL, or news URL with a host.
|
||||
* Restore the '#' in the address.
|
||||
*/
|
||||
*(parts->anchor - 1) = '#';
|
||||
parts->anchor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NOT_DEFINED /* search is just treated as part of path */
|
||||
{
|
||||
char *p = (relative ? relative : absolute);
|
||||
if (p != NULL)
|
||||
{
|
||||
char *q = strchr (p, '?'); /* Any search string? */
|
||||
if (q != NULL)
|
||||
{
|
||||
*q = '\0'; /* If so, chop that off. */
|
||||
parts->search = (q + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* NOT_DEFINED */
|
||||
} /*scan */
|
||||
|
||||
|
||||
/* Parse a Name relative to another name. HTParse()
|
||||
** --------------------------------------
|
||||
**
|
||||
** This returns those parts of a name which are given (and requested)
|
||||
** substituting bits from the related name where necessary.
|
||||
**
|
||||
** On entry,
|
||||
** aName A filename given
|
||||
** relatedName A name relative to which aName is to be parsed
|
||||
** wanted A mask for the bits which are wanted.
|
||||
**
|
||||
** On exit,
|
||||
** returns A pointer to a malloc'd string which MUST BE FREED
|
||||
*/
|
||||
PUBLIC char *HTParse ARGS3 (
|
||||
CONST char *, aName,
|
||||
CONST char *, relatedName,
|
||||
int, wanted)
|
||||
{
|
||||
char *result = NULL;
|
||||
char *return_value = NULL;
|
||||
int len;
|
||||
char *name = NULL;
|
||||
char *rel = NULL;
|
||||
char *p;
|
||||
char *access;
|
||||
struct struct_parts given, related;
|
||||
|
||||
if (TRACE)
|
||||
fprintf (stderr,
|
||||
"HTParse: aName:%s relatedName:%s\n", aName, relatedName);
|
||||
|
||||
/*
|
||||
** Allocate the output string.
|
||||
*/
|
||||
len = strlen (aName) + strlen (relatedName) + 10;
|
||||
result = (char *) malloc (len); /* Lots of space: more than enough */
|
||||
if (result == NULL)
|
||||
outofmem (__FILE__, "HTParse");
|
||||
result[0] = '\0'; /* Clear string */
|
||||
|
||||
/*
|
||||
** Make working copies of the input strings to cut up.
|
||||
*/
|
||||
StrAllocCopy (name, aName);
|
||||
StrAllocCopy (rel, relatedName);
|
||||
|
||||
/*
|
||||
** Cut up the strings into URL fields.
|
||||
*/
|
||||
scan (name, &given);
|
||||
scan (rel, &related);
|
||||
|
||||
/*
|
||||
** Handle the scheme (access) field.
|
||||
*/
|
||||
if (given.access && given.host && !given.relative && !given.absolute)
|
||||
{
|
||||
if (!strcmp (given.access, "http") ||
|
||||
!strcmp (given.access, "https") ||
|
||||
!strcmp (given.access, "ftp"))
|
||||
/*
|
||||
** Assume root.
|
||||
*/
|
||||
given.absolute = "";
|
||||
}
|
||||
access = given.access ? given.access : related.access;
|
||||
if (wanted & PARSE_ACCESS)
|
||||
{
|
||||
if (access)
|
||||
{
|
||||
strcat (result, access);
|
||||
if (wanted & PARSE_PUNCTUATION)
|
||||
strcat (result, ":");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If different schemes, inherit nothing.
|
||||
**
|
||||
** We'll try complying with RFC 1808 and
|
||||
** the Fielding draft, and inherit nothing
|
||||
** if both schemes are given, rather than
|
||||
** only when they differ, except for
|
||||
** file URLs - FM
|
||||
**
|
||||
** After trying it for a while, it's still
|
||||
** premature, IHMO, to go along with it, so
|
||||
** this is back to inheriting for identical
|
||||
** schemes whether or not they are "file".
|
||||
** If you want to try it again yourself,
|
||||
** uncomment the strncasecomp() below. - FM
|
||||
*/
|
||||
if ((given.access && related.access) &&
|
||||
( /* strcasecomp(given.access, "file") || */
|
||||
strcmp (given.access, related.access)))
|
||||
{
|
||||
related.host = NULL;
|
||||
related.absolute = NULL;
|
||||
related.relative = NULL;
|
||||
related.anchor = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle the host field.
|
||||
*/
|
||||
if (wanted & PARSE_HOST)
|
||||
if (given.host || related.host)
|
||||
{
|
||||
char *tail = result + strlen (result);
|
||||
if (wanted & PARSE_PUNCTUATION)
|
||||
strcat (result, "//");
|
||||
strcat (result, given.host ? given.host : related.host);
|
||||
#define CLEAN_URLS
|
||||
#ifdef CLEAN_URLS
|
||||
/*
|
||||
** Ignore default port numbers, and trailing dots on FQDNs,
|
||||
** which will only cause identical addresses to look different.
|
||||
*/
|
||||
{
|
||||
char *p, *h;
|
||||
p = strchr (tail, ':');
|
||||
if (p != NULL && !isdigit ((unsigned char) p[1]))
|
||||
/*
|
||||
** Colon not followed by a port number.
|
||||
*/
|
||||
*p = '\0';
|
||||
if (p != NULL && p != '\0' && access != NULL)
|
||||
{
|
||||
/*
|
||||
** Port specified.
|
||||
*/
|
||||
if ((!strcmp (access, "http") && !strcmp (p, ":80")) ||
|
||||
(!strcmp (access, "gopher") && !strcmp (p, ":70")) ||
|
||||
(!strcmp (access, "ftp") && !strcmp (p, ":21")) ||
|
||||
(!strcmp (access, "wais") && !strcmp (p, ":210")) ||
|
||||
(!strcmp (access, "nntp") && !strcmp (p, ":119")) ||
|
||||
(!strcmp (access, "news") && !strcmp (p, ":119")) ||
|
||||
(!strcmp (access, "snews") && !strcmp (p, ":563")) ||
|
||||
(!strcmp (access, "finger") && !strcmp (p, ":79")) ||
|
||||
(!strcmp (access, "cso") && !strcmp (p, ":105")))
|
||||
*p = '\0'; /* It is the default: ignore it */
|
||||
}
|
||||
if (p == NULL)
|
||||
{
|
||||
int len = strlen (tail);
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
h = tail + len - 1; /* last char of hostname */
|
||||
if (*h == '.')
|
||||
*h = '\0'; /* chop final . */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
h = p;
|
||||
h--; /* End of hostname */
|
||||
if (*h == '.')
|
||||
{
|
||||
/*
|
||||
** Slide p over h.
|
||||
*/
|
||||
while (*p != '\0')
|
||||
*h++ = *p++;
|
||||
*h = '\0'; /* terminate */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CLEAN_URLS */
|
||||
}
|
||||
|
||||
/*
|
||||
** If different hosts, inherit no path.
|
||||
*/
|
||||
if (given.host && related.host)
|
||||
if (strcmp (given.host, related.host) != 0)
|
||||
{
|
||||
related.absolute = NULL;
|
||||
related.relative = NULL;
|
||||
related.anchor = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle the path.
|
||||
*/
|
||||
if (wanted & PARSE_PATH)
|
||||
{
|
||||
if (access && !given.absolute && given.relative)
|
||||
{
|
||||
if (!strcasecomp (access, "nntp") ||
|
||||
!strcasecomp (access, "snews") ||
|
||||
(!strcasecomp (access, "news") &&
|
||||
!strncasecomp (result, "news://", 7)))
|
||||
{
|
||||
/*
|
||||
* Treat all given nntp or snews paths,
|
||||
* or given paths for news URLs with a host,
|
||||
* as absolute.
|
||||
*/
|
||||
given.absolute = given.relative;
|
||||
given.relative = NULL;
|
||||
}
|
||||
}
|
||||
if (given.absolute)
|
||||
{ /* All is given */
|
||||
if (wanted & PARSE_PUNCTUATION)
|
||||
strcat (result, "/");
|
||||
strcat (result, given.absolute);
|
||||
if (TRACE)
|
||||
fprintf (stderr, "1\n");
|
||||
}
|
||||
else if (related.absolute)
|
||||
{ /* Adopt path not name */
|
||||
strcat (result, "/");
|
||||
strcat (result, related.absolute);
|
||||
if (given.relative)
|
||||
{
|
||||
p = strchr (result, '?'); /* Search part? */
|
||||
if (p == NULL)
|
||||
p = (result + strlen (result) - 1);
|
||||
for (; *p != '/'; p--)
|
||||
; /* last / */
|
||||
p[1] = '\0'; /* Remove filename */
|
||||
strcat (result, given.relative); /* Add given one */
|
||||
HTSimplify (result);
|
||||
}
|
||||
if (TRACE)
|
||||
fprintf (stderr, "2\n");
|
||||
}
|
||||
else if (given.relative)
|
||||
{
|
||||
strcat (result, given.relative); /* what we've got */
|
||||
if (TRACE)
|
||||
fprintf (stderr, "3\n");
|
||||
}
|
||||
else if (related.relative)
|
||||
{
|
||||
strcat (result, related.relative);
|
||||
if (TRACE)
|
||||
fprintf (stderr, "4\n");
|
||||
}
|
||||
else
|
||||
{ /* No inheritance */
|
||||
if (strncasecomp (aName, "lynxcgi:", 8) &&
|
||||
strncasecomp (aName, "lynxexec:", 9) &&
|
||||
strncasecomp (aName, "lynxprog:", 9))
|
||||
{
|
||||
strcat (result, "/");
|
||||
}
|
||||
if (!strcmp (result, "news:/"))
|
||||
result[5] = '*';
|
||||
if (TRACE)
|
||||
fprintf (stderr, "5\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle the fragment (anchor).
|
||||
*/
|
||||
if (wanted & PARSE_ANCHOR)
|
||||
if ((given.anchor && *given.anchor) ||
|
||||
(!given.anchor && related.anchor))
|
||||
{
|
||||
if (wanted & PARSE_PUNCTUATION)
|
||||
strcat (result, "#");
|
||||
strcat (result, (given.anchor) ?
|
||||
given.anchor : related.anchor);
|
||||
}
|
||||
if (TRACE)
|
||||
fprintf (stderr, "HTParse: result:%s\n", result);
|
||||
FREE (rel);
|
||||
FREE (name);
|
||||
|
||||
StrAllocCopy (return_value, result);
|
||||
FREE (result);
|
||||
|
||||
return return_value; /* exactly the right length */
|
||||
}
|
||||
|
||||
/* Simplify a filename. HTSimplify()
|
||||
** --------------------
|
||||
**
|
||||
** A unix-style file is allowed to contain the seqeunce xxx/../ which may
|
||||
** be replaced by "" , and the seqeunce "/./" which may be replaced by "/".
|
||||
** Simplification helps us recognize duplicate filenames.
|
||||
**
|
||||
** Thus, /etc/junk/../fred becomes /etc/fred
|
||||
** /etc/junk/./fred becomes /etc/junk/fred
|
||||
**
|
||||
** but we should NOT change
|
||||
** http://fred.xxx.edu/../..
|
||||
**
|
||||
** or ../../albert.html
|
||||
*/
|
||||
PUBLIC void HTSimplify ARGS1 (
|
||||
char *, filename)
|
||||
{
|
||||
char *p;
|
||||
char *q, *q1;
|
||||
|
||||
if (filename == NULL)
|
||||
return;
|
||||
|
||||
if ((filename[0] && filename[1]) && strchr (filename, '/') != NULL)
|
||||
{
|
||||
for (p = (filename + 2); *p; p++)
|
||||
{
|
||||
if (*p == '/')
|
||||
{
|
||||
if ((p[1] == '.') && (p[2] == '.') &&
|
||||
(p[3] == '/' || p[3] == '\0'))
|
||||
{
|
||||
/*
|
||||
** Handle "/../" or "/..".
|
||||
*/
|
||||
for (q = (p - 1); (q >= filename) && (*q != '/'); q--)
|
||||
/*
|
||||
** Back up to previous slash or beginning of string.
|
||||
*/
|
||||
;
|
||||
if ((q[0] == '/') && strncmp (q, "/../", 4) &&
|
||||
!((q - 1) > filename && q[-1] == '/'))
|
||||
{
|
||||
/*
|
||||
** Not at beginning of string or in a
|
||||
** host field, so remove the "/xxx/..".
|
||||
*/
|
||||
q1 = (p + 3);
|
||||
p = q;
|
||||
while (*q1 != '\0')
|
||||
*p++ = *q1++;
|
||||
*p = '\0'; /* terminate */
|
||||
#ifdef NOTDEFINED
|
||||
/*
|
||||
** Make sure filename has at least one slash.
|
||||
*/
|
||||
if (*filename == '\0')
|
||||
{
|
||||
*filename = '/';
|
||||
*(filename + 1) = '\0';
|
||||
}
|
||||
#endif /* NOTDEFINED */
|
||||
/*
|
||||
** Start again with previous slash.
|
||||
*/
|
||||
p = (q - 1);
|
||||
}
|
||||
}
|
||||
else if (p[1] == '.' && p[2] == '/')
|
||||
{
|
||||
/*
|
||||
** Handle "./" by removing the characters.
|
||||
*/
|
||||
q = p;
|
||||
q1 = (p + 2);
|
||||
while (*q1 != '\0')
|
||||
*q++ = *q1++;
|
||||
*q = '\0'; /* terminate */
|
||||
p--;
|
||||
}
|
||||
else if (p[1] == '.' && p[2] == '\0')
|
||||
{
|
||||
/*
|
||||
** Handle terminal "." by removing the character.
|
||||
*/
|
||||
p[1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make Relative Name. HTRelative()
|
||||
** -------------------
|
||||
**
|
||||
** This function creates and returns a string which gives an expression of
|
||||
** one address as related to another. Where there is no relation, an absolute
|
||||
** address is retured.
|
||||
**
|
||||
** On entry,
|
||||
** Both names must be absolute, fully qualified names of nodes
|
||||
** (no anchor bits)
|
||||
**
|
||||
** On exit,
|
||||
** The return result points to a newly allocated name which, if
|
||||
** parsed by HTParse relative to relatedName, will yield aName.
|
||||
** The caller is responsible for freeing the resulting name later.
|
||||
**
|
||||
*/
|
||||
PUBLIC char *HTRelative ARGS2 (
|
||||
CONST char *, aName,
|
||||
CONST char *, relatedName)
|
||||
{
|
||||
char *result = NULL;
|
||||
CONST char *p = aName;
|
||||
CONST char *q = relatedName;
|
||||
CONST char *after_access = NULL;
|
||||
CONST char *path = NULL;
|
||||
CONST char *last_slash = NULL;
|
||||
int slashes = 0;
|
||||
|
||||
for (; *p; p++, q++)
|
||||
{ /* Find extent of match */
|
||||
if (*p != *q)
|
||||
break;
|
||||
if (*p == ':')
|
||||
after_access = p + 1;
|
||||
if (*p == '/')
|
||||
{
|
||||
last_slash = p;
|
||||
slashes++;
|
||||
if (slashes == 3)
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* q, p point to the first non-matching character or zero */
|
||||
|
||||
if (!after_access)
|
||||
{ /* Different access */
|
||||
StrAllocCopy (result, aName);
|
||||
}
|
||||
else if (slashes < 3)
|
||||
{ /* Different nodes */
|
||||
StrAllocCopy (result, after_access);
|
||||
}
|
||||
else if (slashes == 3)
|
||||
{ /* Same node, different path */
|
||||
StrAllocCopy (result, path);
|
||||
}
|
||||
else
|
||||
{ /* Some path in common */
|
||||
int levels = 0;
|
||||
for (; *q && (*q != '#'); q++)
|
||||
if (*q == '/')
|
||||
levels++;
|
||||
result = (char *) malloc (3 * levels + strlen (last_slash) + 1);
|
||||
if (result == NULL)
|
||||
outofmem (__FILE__, "HTRelative");
|
||||
result[0] = '\0';
|
||||
for (; levels; levels--)
|
||||
strcat (result, "../");
|
||||
strcat (result, last_slash + 1);
|
||||
}
|
||||
if (TRACE)
|
||||
fprintf (stderr, "HT: `%s' expressed relative to\n `%s' is\n `%s'.",
|
||||
aName, relatedName, result);
|
||||
return result;
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/* HTParse: URL parsing in the WWW Library
|
||||
** HTPARSE
|
||||
**
|
||||
** This module of the WWW library contains code to parse URLs and various
|
||||
** related things.
|
||||
** Implemented by HTParse.c .
|
||||
*/
|
||||
|
||||
#include "echoping.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef HTPARSE_H
|
||||
#define HTPARSE_H
|
||||
|
||||
/*
|
||||
** The following are flag bits which may be ORed together to form
|
||||
** a number to give the 'wanted' argument to HTParse.
|
||||
*/
|
||||
#define PARSE_ACCESS 16
|
||||
#define PARSE_HOST 8
|
||||
#define PARSE_PATH 4
|
||||
#define PARSE_ANCHOR 2
|
||||
#define PARSE_PUNCTUATION 1
|
||||
#define PARSE_ALL 31
|
||||
|
||||
/*
|
||||
** The following are valid mask values. The terms are the BNF names
|
||||
** in the URL document.
|
||||
*/
|
||||
#define URL_XALPHAS (unsigned char) 1
|
||||
#define URL_XPALPHAS (unsigned char) 2
|
||||
#define URL_PATH (unsigned char) 4
|
||||
|
||||
/*
|
||||
|
||||
Macros for declarations
|
||||
|
||||
*/
|
||||
#define PUBLIC /* Accessible outside this module */
|
||||
#define PRIVATE static /* Accessible only within this module */
|
||||
|
||||
#ifdef __STDC__
|
||||
#define CONST const /* "const" only exists in STDC */
|
||||
#else
|
||||
#define CONST
|
||||
#endif
|
||||
#define NOPARAMS (void)
|
||||
#define PARAMS(parameter_list) parameter_list
|
||||
#define NOARGS (void)
|
||||
#define ARGS1(t,a) \
|
||||
(t a)
|
||||
#define ARGS2(t,a,u,b) \
|
||||
(t a, u b)
|
||||
#define ARGS3(t,a,u,b,v,c) \
|
||||
(t a, u b, v c)
|
||||
|
||||
|
||||
/* Parse a Name relative to another name. HTParse()
|
||||
** --------------------------------------
|
||||
**
|
||||
** This returns those parts of a name which are given (and requested)
|
||||
** substituting bits from the related name where necessary.
|
||||
**
|
||||
** On entry,
|
||||
** aName A filename given
|
||||
** relatedName A name relative to which aName is to be parsed
|
||||
** wanted A mask for the bits which are wanted.
|
||||
**
|
||||
** On exit,
|
||||
** returns A pointer to a malloc'd string which MUST BE FREED
|
||||
*/
|
||||
extern char *HTParse PARAMS((
|
||||
CONST char *aName,
|
||||
CONST char *relatedName,
|
||||
int wanted));
|
||||
|
||||
#ifndef TOLOWER
|
||||
/* Pyramid and Mips can't uppercase non-alpha */
|
||||
#define TOLOWER(c) (isupper((unsigned char)c) ? tolower((unsigned char)c) : (c))
|
||||
#define TOUPPER(c) (islower((unsigned char)c) ? toupper((unsigned char)c) : (c))
|
||||
#endif /* ndef TOLOWER */
|
||||
|
||||
#define outofmem(file, func)\
|
||||
{ fprintf(stderr,\
|
||||
"\r\n\r\n\r\n%s %s: out of memory. Aborting...\r\n", file, func);\
|
||||
exit(-1);}
|
||||
|
||||
#define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
|
||||
#define StrAllocCat(dest, src) HTSACat (&(dest), src)
|
||||
|
||||
PUBLIC void HTSimplify ARGS1 (
|
||||
char *, filename);
|
||||
|
||||
#endif /* HTPARSE_H */
|
||||
|
||||
/*
|
||||
end of HTParse
|
||||
*/
|
@ -0,0 +1,42 @@
|
||||
echoping appears to compile and run at least on OSF/1, Solaris, Linux, SunOS,
|
||||
FreeBSD, IRIX and Ultrix. You do not have to be root to install it.
|
||||
|
||||
Check the Makefile if you wish (C compiler, libraries to include, there are
|
||||
comments to guide you),
|
||||
just type "make",
|
||||
and copy the "echoping" executable anywhere you want.
|
||||
|
||||
Same thing for the man page echoping.1. (You can do an automatic install
|
||||
with "make install" but this depends on the "install" program which is
|
||||
not portable.)
|
||||
|
||||
You can define the following in the Makefile (in CFLAGS):
|
||||
|
||||
HTTP
|
||||
HyperText Transport Protocol support
|
||||
|
||||
ICP
|
||||
ICP protocol support (for Squid Web proxy/cache)
|
||||
|
||||
TTCP
|
||||
T/TCP support if your system supports it (FreeBSD does). See
|
||||
<http://www.noao.edu/~rstevens/ttcp.html>
|
||||
|
||||
USE_SIGACTION
|
||||
Use sigaction instead of signal. On purely BSD systems, such as SunOS or
|
||||
FreeBSD, this is necessary because we need to change the semantics of
|
||||
signals. On some systems (Irix, Solaris), do not use this option, because
|
||||
it will prevent echoping to compile (this is a bug).
|
||||
|
||||
If 'echoping -h' fails with "tcp_open: unknown service: http/tcp", add
|
||||
"http" to the services database (typically /etc/services or a NIS map).
|
||||
Its value is 80. Or, change HTTP_TCP_PORT in echoping.h from "http" to "www"
|
||||
or whatever is defined on your system (at least AIX or Solaris have the
|
||||
problem). A workaround is to specify the port on the command line:
|
||||
echoping -h / www.mydomain.org:80
|
||||
|
||||
If 'echoping -h' replies with a "404" error while the file really exists,
|
||||
check first that you use the FQDN of the server on the command line
|
||||
(this is a consequence of the HTTP 1.1 protocol, not a bug in echoping
|
||||
and this will show only if the HTTP server uses "virtual hosting").
|
||||
|
@ -0,0 +1,5 @@
|
||||
www.obspm.fr
|
||||
www.u-tokyo.ac.jp
|
||||
www.fuw.edu.pl
|
||||
www.fuw.edu.pl
|
||||
www.kaist.ac.kr
|
@ -0,0 +1,76 @@
|
||||
# Choose your C compiler
|
||||
CC = gcc
|
||||
#CC = cc
|
||||
|
||||
# Possible options:
|
||||
# HTTP : supports the HTTP protocol (Add HTTP10 is for the old HTTP 1.0 protocol,
|
||||
# HTTP 0.9 is not supported)
|
||||
# TTCP : supports the T/TCP protocol (few systems support it)
|
||||
# ICP : supports the ICP protocol (Web proxy/caches). Requires HTTP.
|
||||
# USE_SIGACTION: uses sigaction instead of signal. Necessary on pure BSD
|
||||
# machines because we need to change the semantic of signals.
|
||||
OPTIONS = -DHTTP -DUSE_SIGACTION
|
||||
|
||||
# Flags for gcc
|
||||
CFLAGS = -c -O3 $(OPTIONS) -Wall
|
||||
# Flags for cc
|
||||
#CFLAGS = -c -O $(OPTIONS)
|
||||
|
||||
# Flags for the linker
|
||||
LD = $(CC)
|
||||
LDFLAGS = -o echoping
|
||||
# For Solaris
|
||||
#LDFLAGS = -o echoping -lsocket -lnsl
|
||||
|
||||
INSTALL=install
|
||||
INSTALL_BIN_FLAGS=-m 755
|
||||
INSTALL_MAN_FLAGS=-m 644
|
||||
|
||||
ROOT=/usr/local
|
||||
DESTBIN=$(ROOT)/bin
|
||||
DESTMAN=$(ROOT)/man/man1
|
||||
|
||||
########## Do not touch below this line #########
|
||||
|
||||
OBJS = echoping.o error.o readline.o writen.o util.o http.o icp.o HTParse.o
|
||||
HEADERS = echoping.h icp.h HTParse.h
|
||||
SOURCES = echoping.c error.c readline.c writen.c util.c http.c icp.c HTParse.c $(HEADERS)
|
||||
MISC = README Makefile
|
||||
DISTRIB= README INSTALL CHANGES DETAILS CREDITS Makefile echoping.ptk $(SOURCES) echoping.1
|
||||
VERSION=`grep VERSION echoping.h | cut -d ' ' -f 3 | sed s/\"//g`
|
||||
|
||||
all: echoping
|
||||
|
||||
echoping: $(OBJS)
|
||||
@ echo Linking $@ with new $?
|
||||
$(LD) $(LDFLAGS) $(OBJS)
|
||||
|
||||
.c.o: $(HEADERS)
|
||||
$(CC) $(CFLAGS) $<
|
||||
|
||||
clean:
|
||||
-rm echoping $(OBJS)
|
||||
@ echo Erased
|
||||
|
||||
distrib.old:
|
||||
@(cd .. ; \
|
||||
tar cvf "echoping/echoping.tar" $(DISTRIB); \
|
||||
gzip -v -9 -f "echoping/echoping.tar"; \
|
||||
uuencode echoping/echoping.tar.gz echoping.tar.gz > echoping/echoping.tar.gz.uu)
|
||||
|
||||
distrib:
|
||||
@(echo Echoping is version ${VERSION}; \
|
||||
mkdir echoping-${VERSION}; \
|
||||
cp $(DISTRIB) echoping-${VERSION};\
|
||||
tar cvf echoping-${VERSION}.tar echoping-${VERSION}; \
|
||||
rm -rf echoping-${VERSION}; \
|
||||
gzip -v -9 -f echoping-${VERSION}.tar; \
|
||||
uuencode echoping-${VERSION}.tar.gz echoping-${VERSION}.tar.gz > \
|
||||
echoping-${VERSION}.tar.gz.uu)
|
||||
|
||||
checkout:
|
||||
co -l $(SOURCES) README
|
||||
|
||||
install: echoping
|
||||
$(INSTALL) $(INSTALL_BIN_FLAGS) echoping $(DESTBIN)
|
||||
$(INSTALL) $(INSTALL_MAN_FLAGS) echoping.1 $(DESTMAN)
|
@ -0,0 +1,18 @@
|
||||
bin_PROGRAMS = echoping
|
||||
HEADERS = echoping.h icp.h HTParse.h
|
||||
echoping_SOURCES = echoping.c error.c readline.c writen.c util.c http.c icp.c HTParse.c $(HEADERS)
|
||||
echoping_LDADD =
|
||||
|
||||
distrib:
|
||||
@(echo Echoping is version ${VERSION}; \
|
||||
mkdir echoping-${VERSION}; \
|
||||
cp $(DISTRIB) echoping-${VERSION};\
|
||||
tar cvf echoping-${VERSION}.tar echoping-${VERSION}; \
|
||||
rm -rf echoping-${VERSION}; \
|
||||
gzip -v -9 -f echoping-${VERSION}.tar; \
|
||||
uuencode echoping-${VERSION}.tar.gz echoping-${VERSION}.tar.gz > \
|
||||
echoping-${VERSION}.tar.gz.uu)
|
||||
|
||||
checkout:
|
||||
co -l $(SOURCES) README
|
||||
|
@ -0,0 +1,67 @@
|
||||
"echoping" is a small program to test (approximatively) performances of a
|
||||
remote host by sending it TCP "echo" (or other protocol) packets.
|
||||
|
||||
To install it, see the INSTALL file. Or type "make" if you're in a
|
||||
hurry :-)
|
||||
|
||||
To use it, simply:
|
||||
|
||||
% echoping machine.somewhere.org
|
||||
|
||||
or use the options before the machine name (see the man page).
|
||||
|
||||
See the DETAILS file for various traps when benchmarking networks,
|
||||
specially with this program
|
||||
|
||||
In any case, be polite: don't bother the remote host with many repeated
|
||||
requests, especially with large size. Ask for permission if you often
|
||||
test hosts which aren't yours.
|
||||
|
||||
Current features:
|
||||
|
||||
- uses the protocols echo, discard, chargen or HTTP,
|
||||
- uses UDP instead of TCP for the protocols which accept it (like echo),
|
||||
- can repeat the test and display various measures about it,
|
||||
- can use T/TCP on systems which support it.
|
||||
|
||||
Examples of output:
|
||||
|
||||
(Simple test with 1000 bytes echo TCP packets)
|
||||
% echoping -v -s 1000 mycisco
|
||||
This is echoping, version 2.0.
|
||||
Trying to connect to internet address 10.99.64.1 to transmit 256 bytes...
|
||||
Connected...
|
||||
Sent (1000 bytes)...
|
||||
Checked
|
||||
Elapsed time: 0.059597 seconds
|
||||
%
|
||||
|
||||
(Repeated tests with average and median displayed.)
|
||||
% echoping -n 10 mymachine
|
||||
[...]
|
||||
Minimum time: 0.170719 seconds (1500 bytes per sec.)
|
||||
Maximum time: 0.211176 seconds (1212 bytes per sec.)
|
||||
Average time: 0.184577 seconds (1387 bytes per sec.)
|
||||
Median time: 0.181332 seconds (1412 bytes per sec.)
|
||||
|
||||
(Testing a Web server with an HTTP request for its home page.)
|
||||
% echoping -h / mywww
|
||||
Elapsed time: 0.686792 seconds
|
||||
|
||||
(The exit status is set if there is any problem, so you can use echoping
|
||||
to test repeatedly a Web server, to be sure it runs fine.)
|
||||
|
||||
|
||||
To do for a future version:
|
||||
|
||||
- display other calculations such as standard deviation
|
||||
- fix the bugs (everlasting aim)
|
||||
|
||||
-------------
|
||||
The reference site for echoping is:
|
||||
|
||||
ftp://ftp.internatif.org/pub/unix/echoping
|
||||
|
||||
Stephane Bortzmeyer <bortzmeyer@pasteur.fr>. October 1995 for the
|
||||
first version. August 1998 for this one.
|
||||
|
@ -0,0 +1,11 @@
|
||||
A faire pour la version 2.1 :
|
||||
|
||||
TCP timeout et T/TCP
|
||||
|
||||
TCP timeout et longs paquets (Cisco)
|
||||
|
||||
Tester sur plein (Linux, Irix, SunOS, FreeBSD)
|
||||
|
||||
Bogue HTTP/1.0 si proxy
|
||||
|
||||
Bogue ICP : répond toujours même si timeout, etc
|
Binary file not shown.
@ -0,0 +1,109 @@
|
||||
.TH echoping 1 "November 22, 1996" "ECHOPING" "echoping"
|
||||
|
||||
.SH NAME
|
||||
echoping \- tests a remote host with TCP or UDP
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B echoping
|
||||
.RI [ -vudcr ]
|
||||
.RI [ -s\ number ]
|
||||
.RI [ -n\ number ]
|
||||
.RI [ -w\ number ]
|
||||
.RI [ -t\ number ]
|
||||
.RI [ -h\ URL ]
|
||||
.RI [ -i\ URL ]
|
||||
.B hostname
|
||||
[:port]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.LP
|
||||
.B echoping
|
||||
is a small program to test (approximatively) performances
|
||||
of a remote Internet host by sending it TCP "echo" packets. It can use other
|
||||
protocols as well (HTTP, UDP "echo", etc).
|
||||
.LP
|
||||
.B echoping
|
||||
simply shows the elapsed time, including the time to set up the TCP
|
||||
connection and to transfer the data. Therefore, it is unsuitable to physical
|
||||
line raw throughput measures (unlike bing or treno). On the other end, the
|
||||
action it performs are close from, for instance, a HTTP request and it is meaningful
|
||||
to use it (carefully) to measure Web performances.
|
||||
|
||||
.SH ARGUMENT
|
||||
.IP hostname[:port]
|
||||
Name of the server to test. For HTTP, you can specify a port number.
|
||||
|
||||
.SH OPTIONS
|
||||
.IP -v
|
||||
Verbose
|
||||
.IP -s\ nnn
|
||||
Size of the data to send. Large values can produce strange results with
|
||||
some echo servers.
|
||||
.IP -n\ nnn
|
||||
Numbers of repeated tests. With this option, you have also the minimum, maximum, average and median
|
||||
time. The median is the value such that half of the measures are under it
|
||||
and the other half is above. When you measure highly variables values, like
|
||||
it is often the case on the whole Internet, median is better than average
|
||||
to avoid "extreme" values.
|
||||
.IP -w\ nnn
|
||||
Number of seconds to wait between two tests (default is one)
|
||||
.IP -t\ nnn
|
||||
Number of seconds to wait a reply before giving up. For TCP, this is the
|
||||
maximum number of seconds for the whole connection (setup and data exchange).
|
||||
.IP -u
|
||||
Use UDP instead of TCP
|
||||
.IP -d
|
||||
Use the "discard" service instead of echo
|
||||
.IP -c
|
||||
Use the "chargen" service instead of echo
|
||||
.IP -h\ url
|
||||
Use the HTTP protocol (instead of echo) for the given URL. The URL has to
|
||||
be a relative one (for instance '/' or '/pics/foobar.gif') because HTTP 1.0
|
||||
servers will not understand a request for an absolute URL.
|
||||
.IP -i\ url
|
||||
Use the ICP protocol (instead of echo) for the given URL. The URL has to
|
||||
be an absolute one. This is mostly for testing Squid Web proxy/caches.
|
||||
.IP -r
|
||||
Use T/TCP (if it has been compiled with it). See the INSTALL file for details.
|
||||
.IP -f\ character
|
||||
Fill the packet with this character (default is random filling)
|
||||
|
||||
.SH EXAMPLES
|
||||
.IP echoping\ \-v\ foobar.whoops.org
|
||||
Tests the remote machine with TCP echo (one test).
|
||||
.IP echoping\ \-n\ 5\ \-w\ 10\ foobar.whoops.org
|
||||
Tests the remote machine with TCP echo (five tests, every ten seconds).
|
||||
.IP echoping\ \-h\ /\ foobar.whoops.org
|
||||
Tests the remote Web server and asks its home page. Note you don't
|
||||
indicate the whole URL.
|
||||
.IP echoping\ \-h\ http://www.whoops.org/\ cache.whoops.org:3128
|
||||
Tests the remote Web proxy-cache and asks a Web page. Note that you must
|
||||
indicate the whole URL.
|
||||
|
||||
.SH BUGS
|
||||
UDP isn't really useable with large packets because of sockets
|
||||
limitations and the lack of workaround code.
|
||||
|
||||
ICP support is far from perfect, specially on the Alpha or when
|
||||
something goes wrong (filtering for instance).
|
||||
|
||||
If a Web page is empty,
|
||||
.B echoping
|
||||
will display meaningless messages.
|
||||
|
||||
Timeouts on TCP connections are a bit experimental.
|
||||
|
||||
Since
|
||||
.B echoping
|
||||
writes everything to the server, then begins to read the reply, some echo
|
||||
servers
|
||||
will timeout if you send very long messages, because they expect you to
|
||||
start to read right now.
|
||||
|
||||
.SH SEE ALSO
|
||||
See the README for information about other network measurements programs.
|
||||
|
||||
.SH AUTHOR
|
||||
Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
|
||||
|
||||
|
@ -0,0 +1,834 @@
|
||||
/*
|
||||
* echoping : uses the TCP echo service to measure (roughly) response times.
|
||||
*
|
||||
* Written by Stephane Bortzmeyer <bortzmeyer@pasteur.fr>. A lot of code stolen
|
||||
* from Richard Stevens' book "Unix network programming" and Pierre Beyssac's
|
||||
* "bing" tool.
|
||||
*
|
||||
*/
|
||||
|
||||
char *progname;
|
||||
unsigned short timeout_flag;
|
||||
|
||||
#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
|
||||
|
||||
/* Global variables for main and printstats */
|
||||
|
||||
int return_code = 0;
|
||||
unsigned int number = 1;
|
||||
struct timeval max, min, total, median, temp;
|
||||
unsigned int successes, attempts = 0;
|
||||
unsigned int size = DEFLINE;
|
||||
unsigned int j = 0;
|
||||
struct result
|
||||
{
|
||||
unsigned short valid;
|
||||
struct timeval timevalue;
|
||||
};
|
||||
struct result results[MAXNUMBER];
|
||||
struct timeval good_results[MAXNUMBER];
|
||||
extern int tvcmp ();
|
||||
|
||||
void
|
||||
main (argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
signed char ch;
|
||||
|
||||
int sockfd;
|
||||
struct hostent *hostptr;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct sockaddr_in udp_cli_addr; /* client's Internet socket
|
||||
* addr */
|
||||
struct servent *sp;
|
||||
int verbose = FALSE;
|
||||
char *server_address;
|
||||
u_int addr;
|
||||
struct in_addr *ptr;
|
||||
int n, nr;
|
||||
char *sendline, recvline[MAXLINE + 1];
|
||||
#ifdef ICP
|
||||
char retcode[DEFLINE];
|
||||
int length;
|
||||
#endif
|
||||
struct timeval newtv, oldtv;
|
||||
void printstats ();
|
||||
|
||||
unsigned int wait = 1;
|
||||
unsigned char fill = ' ';
|
||||
unsigned short fill_requested = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
void to_alarm (); /* our alarm() signal handler */
|
||||
void interrupted ();
|
||||
unsigned int timeout = 10;
|
||||
unsigned short timeout_requested = 0;
|
||||
unsigned short size_requested = 0;
|
||||
char *url = "";
|
||||
short port = 0;
|
||||
char *text_port = malloc (6);
|
||||
#if USE_SIGACTION
|
||||
struct sigaction mysigaction;
|
||||
#endif
|
||||
|
||||
char *port_name = ECHO_TCP_PORT;
|
||||
unsigned short port_to_use = USE_ECHO;
|
||||
unsigned short http = 0;
|
||||
unsigned short udp = 0;
|
||||
unsigned short icp = 0;
|
||||
#ifdef ICP
|
||||
icp_opcode opcode = ICP_OP_QUERY;
|
||||
#endif
|
||||
|
||||
unsigned short ttcp = 0;
|
||||
unsigned short tcp = 0;
|
||||
|
||||
unsigned short stop_at_newlines = 1;
|
||||
|
||||
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;
|
||||
|
||||
for (i = 0; i <= MAXNUMBER; i++)
|
||||
{
|
||||
results[i].valid = 0;
|
||||
}
|
||||
progname = argv[0];
|
||||
while ((ch = getopt (argc, argv, "vs:n:w:dch:i:rut:f:")) != EOF)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'v':
|
||||
verbose = TRUE;
|
||||
break;
|
||||
case 'r':
|
||||
ttcp = 1;
|
||||
break;
|
||||
case 'u':
|
||||
udp = 1;
|
||||
break;
|
||||
case 'd':
|
||||
port_name = DISCARD_TCP_PORT;
|
||||
port_to_use = USE_DISCARD;
|
||||
break;
|
||||
case 'c':
|
||||
port_name = CHARACTER_GENERATOR_TCP_PORT;
|
||||
port_to_use = USE_CHARGEN;
|
||||
stop_at_newlines = 0;
|
||||
break;
|
||||
case 'i':
|
||||
port_name = ICP_UDP_PORT;
|
||||
port_to_use = USE_ICP;
|
||||
udp = 1;
|
||||
icp = 1;
|
||||
url = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
port_name = HTTP_TCP_PORT;
|
||||
port_to_use = USE_HTTP;
|
||||
http = 1;
|
||||
url = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
fill = *optarg;
|
||||
fill_requested = 1;
|
||||
break;
|
||||
case 's':
|
||||
size = atoi (optarg);
|
||||
if (size > MAXLINE)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: packet size too large, max is %d.\n",
|
||||
progname, MAXLINE);
|
||||
exit (1);
|
||||
}
|
||||
if (size <= 0)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: illegal packet size.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
size_requested = 1;
|
||||
break;
|
||||
case 't':
|
||||
timeout = atoi (optarg);
|
||||
timeout_requested = 1;
|
||||
if (size <= 0)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: illegal timeout.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
number = atoi (optarg);
|
||||
if (number > MAXNUMBER)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: number of iterations too large, max is %d.\n",
|
||||
progname, MAXNUMBER);
|
||||
exit (1);
|
||||
}
|
||||
if (number <= 0)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: illegal number of iterations.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
wait = atoi (optarg);
|
||||
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;
|
||||
default:
|
||||
usage ();
|
||||
}
|
||||
}
|
||||
if (udp && ((port_to_use == USE_CHARGEN) || (port_to_use == USE_HTTP)))
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: I don't know how to use this port with UDP.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
/*
|
||||
Version 2.1 now allows global timeouts for TCP connections
|
||||
*
|
||||
if (!udp && (timeout_requested))
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: Time out ignored for TCP connections.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
*/
|
||||
if (http && (fill_requested))
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: Filling incompatible with HTTP connections.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
#ifndef TTCP
|
||||
if (ttcp)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: not compiled with T/TCP 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 ICP
|
||||
if (icp)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: Not compiled with ICP support.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
#endif
|
||||
if (http && size_requested)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: HTTP and message size specification are incompatible.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
if (udp && ttcp)
|
||||
{
|
||||
(void) fprintf (stderr,
|
||||
"%s: UDP and T/TCP are incompatible.\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
if (!udp && !ttcp)
|
||||
{
|
||||
tcp = 1;
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc != 1)
|
||||
{
|
||||
usage ();
|
||||
}
|
||||
if (verbose)
|
||||
{
|
||||
printf ("\nThis is %s, version %s.\n\n", progname, VERSION);
|
||||
}
|
||||
server = argv[0];
|
||||
#ifdef HTTP
|
||||
if (http || icp)
|
||||
{
|
||||
if (icp)
|
||||
{
|
||||
find_server_and_port (server, &port, port_name);
|
||||
if (port == 0)
|
||||
port = 3130;
|
||||
}
|
||||
else
|
||||
{
|
||||
find_server_and_port (server, &port, port_name);
|
||||
if (port == 0)
|
||||
port = 80;
|
||||
}
|
||||
|
||||
sprintf (text_port, "(port %d)", ntohs (port));
|
||||
}
|
||||
#endif
|
||||
signal (SIGINT, interrupted);
|
||||
if ((addr = inet_addr (server)) == INADDR_NONE)
|
||||
{
|
||||
if ((hostptr = gethostbyname (server)) == NULL)
|
||||
{
|
||||
err_quit ("gethostbyname error for host: %s %s",
|
||||
server, sys_err_str ());
|
||||
}
|
||||
server_address = *(hostptr->h_addr_list); /* First item of the
|
||||
* list */
|
||||
/*
|
||||
* addr = (u_long) *server_address;
|
||||
*/
|
||||
/* ptr.s_addr = addr; */
|
||||
ptr = (struct in_addr *) server_address; /* hostptr->h_addr_list
|
||||
* points actually to
|
||||
* u_longs, not strings */
|
||||
addr = ptr->s_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = (struct in_addr *) malloc (sizeof (struct in_addr));
|
||||
ptr->s_addr = addr;
|
||||
}
|
||||
if (!http) /* Already find */
|
||||
{
|
||||
if (!udp)
|
||||
{
|
||||
if ((sp = getservbyname (port_name, "tcp")) == NULL)
|
||||
{
|
||||
err_quit ("tcp_open: unknown service: %s/tcp", port_name);
|
||||
}
|
||||
}
|
||||
else if (!icp)
|
||||
{
|
||||
if ((sp = getservbyname (port_name, "udp")) == NULL)
|
||||
{
|
||||
err_quit ("tcp_open: unknown service: %s/udp", port_name);
|
||||
}
|
||||
}
|
||||
/* Else ICP... */
|
||||
}
|
||||
/*
|
||||
* Fill in the structure "serv_addr" with the address of the server
|
||||
* that we want to connect with.
|
||||
*/
|
||||
|
||||
bzero ((char *) &serv_addr, sizeof (serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = addr;
|
||||
if (!http && !icp)
|
||||
{
|
||||
serv_addr.sin_port = sp->s_port;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv_addr.sin_port = port;
|
||||
}
|
||||
|
||||
#ifdef HTTP
|
||||
if (http)
|
||||
{
|
||||
sendline = make_http_sendline (url, server, (int) ntohs (port));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ICP
|
||||
if (icp)
|
||||
{
|
||||
sendline = make_icp_sendline (url, &addr, opcode, &length);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!fill_requested)
|
||||
{
|
||||
sendline = random_string (size);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendline = (char *) malloc (size);
|
||||
for (i = 0; i < size; i++)
|
||||
sendline[i] = fill;
|
||||
}
|
||||
n = strlen (sendline) + 1;
|
||||
|
||||
for (i = 1; i <= number; i++)
|
||||
{
|
||||
|
||||
attempts++;
|
||||
if (!udp)
|
||||
{
|
||||
/*
|
||||
* Open a TCP socket (an Internet stream socket).
|
||||
*/
|
||||
|
||||
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys ("Can't open stream socket");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys ("Can't open datagram socket");
|
||||
/* Bind socket for reply. Not necessary? */
|
||||
bzero ((char *) &udp_cli_addr, sizeof (udp_cli_addr));
|
||||
udp_cli_addr.sin_family = AF_INET;
|
||||
udp_cli_addr.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
udp_cli_addr.sin_port = htons (0);
|
||||
if (bind (sockfd, (struct sockaddr *) &udp_cli_addr,
|
||||
sizeof (udp_cli_addr)) < 0)
|
||||
{
|
||||
err_sys ("bind error");
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
{
|
||||
if (tcp)
|
||||
{
|
||||
printf ("Trying to connect to internet address %s %s to transmit %u bytes...\n",
|
||||
inet_ntoa (*ptr), (port == 0 ? "" : text_port), n);
|
||||
}
|
||||
#ifdef ICP
|
||||
if (icp)
|
||||
{
|
||||
printf ("Trying to send an ICP packet of %u bytes to the internet address %s...\n",
|
||||
length, inet_ntoa (*ptr));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
printf ("Trying to send %u bytes to internet address %s...\n",
|
||||
size, inet_ntoa (*ptr));
|
||||
}
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush ((FILE *) NULL) != 0)
|
||||
{
|
||||
err_sys ("I cannot flush");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (tcp && 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 (!ttcp && !icp)
|
||||
{
|
||||
/*
|
||||
* Connect to the server.
|
||||
*/
|
||||
|
||||
if (connect (sockfd, (struct sockaddr *) &serv_addr,
|
||||
sizeof (serv_addr)) < 0)
|
||||
{
|
||||
if ((errno == EINTR) && (timeout_flag))
|
||||
{
|
||||
printf ("Timeout while connecting\n");
|
||||
continue;
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush ((FILE *) NULL) != 0)
|
||||
{
|
||||
err_sys ("I cannot flush");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
err_sys ("Can't connect to server");
|
||||
}
|
||||
if (verbose && tcp)
|
||||
{
|
||||
printf ("Connected...\n");
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush ((FILE *) NULL) != 0)
|
||||
{
|
||||
err_sys ("I cannot flush");
|
||||
}
|
||||
#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))
|
||||
{
|
||||
#ifdef TTCP
|
||||
if (ttcp)
|
||||
{
|
||||
if (sendto (sockfd, sendline, n, MSG_EOF,
|
||||
(struct sockaddr *) &serv_addr, sizeof (serv_addr)) != n)
|
||||
err_sys ("sendto error on socket");
|
||||
if (verbose)
|
||||
{
|
||||
printf ("T/TCP connection done\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!udp)
|
||||
{
|
||||
/* 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");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
err_sys ("writen error on socket");
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ICP
|
||||
if (icp)
|
||||
{
|
||||
if (sendto (sockfd, sendline, length, 0,
|
||||
&serv_addr, sizeof (serv_addr)) != 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)
|
||||
{
|
||||
#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 ((port_to_use == USE_ECHO) || (port_to_use == USE_CHARGEN) ||
|
||||
(port_to_use == USE_HTTP) || (port_to_use == USE_ICP))
|
||||
{
|
||||
if (!udp)
|
||||
{
|
||||
if (!http)
|
||||
{
|
||||
/* Read from the server */
|
||||
nr = readline (sockfd, recvline, n, stop_at_newlines);
|
||||
}
|
||||
#ifdef HTTP
|
||||
else
|
||||
{
|
||||
nr = read_from_server (sockfd);
|
||||
}
|
||||
#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.
|
||||
*/
|
||||
/*
|
||||
* BUG: 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)
|
||||
{
|
||||
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
|
||||
continue;
|
||||
}
|
||||
if (nr < 0 || nr != n)
|
||||
err_sys ("readline error: %d bytes read, %d bytes requested", nr, n);
|
||||
}
|
||||
else
|
||||
/* This is 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
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
printf ("%d bytes read from server.\n", nr);
|
||||
}
|
||||
/* That's all, folks */
|
||||
close (sockfd);
|
||||
|
||||
(void) gettimeofday (&newtv, (struct timezone *) NULL);
|
||||
if (tcp)
|
||||
alarm (0);
|
||||
temp = newtv;
|
||||
tvsub (&temp, &oldtv);
|
||||
if (!timeout_flag)
|
||||
{
|
||||
tvadd (&total, &temp);
|
||||
|
||||
/* Check */
|
||||
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)
|
||||
{
|
||||
/* BUG: 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)
|
||||
{
|
||||
sleep (wait);
|
||||
}
|
||||
}
|
||||
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)... */
|
||||
for (i = 0; i < number; i++)
|
||||
{
|
||||
if (results[i].valid)
|
||||
good_results[j++] = results[i].timevalue;
|
||||
}
|
||||
if (successes != j) /* 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);
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Definitions for TCP and UDP client/server programs.
|
||||
*/
|
||||
|
||||
#define VERSION "2.2.1"
|
||||
|
||||
/* Probably too many inclusions but this is to keep 'gcc -Wall' happy... */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <varargs.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef SIGALRM /* Linux... */
|
||||
#define SIGALRM 14 /* alarm clock timeout */
|
||||
#endif
|
||||
#ifndef SIGINT /* Linux... */
|
||||
#define SIGINT 2 /* interrupt, generated from terminal special char */
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_NONE /* SunOS */
|
||||
#define INADDR_NONE (-1)
|
||||
#endif
|
||||
|
||||
/* These entities should be in errno.h but some systems do not define
|
||||
them. */
|
||||
#ifdef sun
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
/* Solaris */
|
||||
#ifdef __svr4__
|
||||
/* Nothing specific yet */
|
||||
#endif /* SVR4 */
|
||||
#endif /* Sun */
|
||||
#ifdef _AIX
|
||||
extern char *sys_nerr[];
|
||||
extern char *sys_errlist[];
|
||||
#endif
|
||||
|
||||
struct timeval null_timeval;
|
||||
struct timeval max_timeval;
|
||||
|
||||
#define ECHO_TCP_PORT "echo"
|
||||
#define DISCARD_TCP_PORT "discard"
|
||||
#define CHARACTER_GENERATOR_TCP_PORT "chargen"
|
||||
#define HTTP_TCP_PORT "http"
|
||||
#define ICP_UDP_PORT "icp"
|
||||
|
||||
#define USE_ECHO 1
|
||||
#define USE_DISCARD 2
|
||||
#define USE_CHARGEN 3
|
||||
#define USE_HTTP 4
|
||||
#define USE_ICP 5
|
||||
|
||||
#define CHARGENERATED " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg";
|
||||
|
||||
char *server;
|
||||
|
||||
/* My functions */
|
||||
|
||||
/* error.c */
|
||||
void usage ();
|
||||
void err_sys ();
|
||||
void err_ret ();
|
||||
void err_quit ();
|
||||
char *sys_err_str ();
|
||||
/* writen.c */
|
||||
int writen ();
|
||||
/* readline.c */
|
||||
int readline ();
|
||||
/* util.c */
|
||||
char *random_string ();
|
||||
void tvsub ();
|
||||
void tvadd ();
|
||||
void tvavg ();
|
||||
void tvmin ();
|
||||
void tvmax ();
|
||||
double tv2double ();
|
||||
/* http.c */
|
||||
#ifdef HTTP
|
||||
char *make_http_sendline ();
|
||||
void find_server_and_port ();
|
||||
int read_from_server ();
|
||||
#endif
|
||||
|
||||
#ifdef ICP
|
||||
#include "icp.h"
|
||||
void *make_icp_sendline ();
|
||||
int recv_icp ();
|
||||
#ifndef HTTP
|
||||
void find_server_and_port ();
|
||||
int read_from_server ();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#define DEFLINE 256
|
||||
#define MAXLINE 65535
|
||||
#define UDPMAX 65535
|
||||
#ifdef HTTP
|
||||
#define MAXTOREAD 150000
|
||||
#endif
|
||||
#define MAXNUMBER 20
|
||||
|
||||
extern char *progname;
|
||||
|
||||
extern unsigned short timeout_flag;
|
@ -0,0 +1,365 @@
|
||||
#!/usr/local/bin/perl -w
|
||||
|
||||
require 5.003;
|
||||
use Tk;
|
||||
use Socket;
|
||||
|
||||
# Let's be paranoid
|
||||
use strict;
|
||||
|
||||
require "newgetopt.pl";
|
||||
&NGetOpt (("geometry=s", "font=s", "background=s", "bg=s", "foreground=s", "fg=s", "title=s"));
|
||||
|
||||
$ENV{'SHELL'} = "/bin/sh";
|
||||
|
||||
my $default_host;
|
||||
if (@ARGV) {
|
||||
$default_host = shift (@ARGV);
|
||||
#TODO: shouldn't we try to echoping it right now?
|
||||
}
|
||||
else {
|
||||
$default_host = "fritz.globenet.org";
|
||||
}
|
||||
if (@ARGV) {
|
||||
print STDERR "Ignoring extra arguments \"" . join (' ', @ARGV) . "\"\n";
|
||||
}
|
||||
|
||||
my $top = MainWindow->new;
|
||||
|
||||
if ($main::opt_geometry) {
|
||||
$top->geometry ($main::opt_geometry);
|
||||
}
|
||||
if ($main::opt_title) {
|
||||
$top->title ($main::opt_title);
|
||||
}
|
||||
else {
|
||||
$top->title ("EchoPing Driver");
|
||||
}
|
||||
#TODO: how to set background, font, etc for all the widgets?
|
||||
if ($main::opt_bg) {
|
||||
$main::opt_background = $main::opt_bg;
|
||||
}
|
||||
if ($main::opt_background) {
|
||||
$top->configure (-background => $main::opt_background);
|
||||
}
|
||||
if ($main::opt_fg) {
|
||||
$main::opt_foreground = $main::opt_fg;
|
||||
}
|
||||
if ($main::opt_foreground) {
|
||||
$top->configure (-foreground => $main::opt_foreground);
|
||||
}
|
||||
if ($main::opt_font) {
|
||||
$top->configure (-font => $main::opt_font);
|
||||
}
|
||||
|
||||
#TODO : on line help with context => 'connection refused' will give an explanation
|
||||
|
||||
$main::echoping = &find_pg ("echoping");
|
||||
if (! $main::echoping) {
|
||||
print STDERR "Cannot find the echoping program in the path.\n";
|
||||
exit 1;
|
||||
#TODO: a nice pop-up window with an hypertext link to the FTP server :-)
|
||||
}
|
||||
|
||||
my $message;
|
||||
open (ECHOPING, "$main::echoping -v localhost 2>&1 |") || &panic ("Cannot echoping");
|
||||
my $result = <ECHOPING>;
|
||||
chop $result;
|
||||
if ($result) { # Something was wrong
|
||||
if ($result =~ /Connection refused/) {
|
||||
$message = "localhost refused echo: egoist!";
|
||||
#TODO: better explanations
|
||||
}
|
||||
else {
|
||||
$message = "Problem localhost: $result";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = <ECHOPING>;
|
||||
}
|
||||
close (ECHOPING);
|
||||
|
||||
# Some useful declarations
|
||||
my $results;
|
||||
my $number;
|
||||
my $size;
|
||||
my $delay;
|
||||
|
||||
my $frame1 = $top->Frame(-borderwidth => '2m');
|
||||
$frame1->pack(-fill => 'x');
|
||||
|
||||
# Entry field
|
||||
my $entry = $frame1->Entry(-relief => 'sunken', -width => 45);
|
||||
my $label = $frame1->Label(-text => 'Enter host name');
|
||||
$label->pack(-side => 'left');
|
||||
$entry->pack(-side => 'left');
|
||||
$entry->insert('insert', $default_host);
|
||||
#$entry->selection ('range', 0, length ($default_host));
|
||||
$entry->focus;
|
||||
# I believe the following binding is necessary only on OSF/1?
|
||||
$entry->bind('<Delete>' => 'Backspace');
|
||||
|
||||
# Doit button
|
||||
my $doit = $frame1->Button(-text => 'Do it',
|
||||
-command => sub {doit ($top, $entry, $results,
|
||||
$number->get, $size->get, $delay->get, $main::text)});
|
||||
$doit->pack(-side => 'left', -fill => 'x', -padx => '2m');
|
||||
$top->bind ('<Return>' => sub {doit ($top, $entry, $results,
|
||||
$number->get, $size->get, $delay->get, $main::text)});
|
||||
my $cancel = $frame1->Button(-text => 'Cancel',
|
||||
#-command => sub {$main::cancel_requested = 1;});
|
||||
#TODO: Cancel should test if an operation is in progress, otherwise, it will
|
||||
# be "recorded" for the next time.
|
||||
-command => sub {cancel_requested ($top, $results);});
|
||||
$cancel->pack(-side => 'left', -fill => 'x', -padx => '2m');
|
||||
|
||||
my $frame2 = $top->Frame(-borderwidth => '2m');
|
||||
$frame2->pack(-fill => 'x');
|
||||
#TODO: every number should be in the settings section at the beginning
|
||||
$number = $frame2->Scale(-from => '1', -to => '10', -orient => 'horizontal', -label => 'Number of connections');
|
||||
$number->set ('1');
|
||||
$number->pack (-side => 'top', -fill => 'x');
|
||||
$size = $frame2->Scale(-from => '1', -to => '1000', '-length' => '500', -orient => 'horizontal', -label => 'Size of packets');
|
||||
$size->set ('256'); #TODO: finds a way to enter value directly
|
||||
$size->pack (-side => 'top', -fill => 'x');
|
||||
$delay = $frame2->Scale(-from => '0', -to => '20', '-length' => '500', -orient => 'horizontal', -label => 'Delay between connections');
|
||||
$delay->set ('1');
|
||||
$delay->pack (-side => 'top', -fill => 'x');
|
||||
|
||||
my $frame3 = $top->Frame(-borderwidth => '2m');
|
||||
$frame3->pack (-fill => 'both', -expand => 'yes');
|
||||
|
||||
# Status text
|
||||
$main::text = $frame3->Label(
|
||||
-justify => 'center',
|
||||
-text => "$message",
|
||||
);
|
||||
$main::text->pack(-side => 'top', -fill => 'none', -expand => 'no');
|
||||
|
||||
# Results text with scrollbar
|
||||
#TODO: nice tags and hypertext tags
|
||||
$results = $frame3->Text(-relief => 'sunken', -state => 'disabled');
|
||||
my $scrollbar = $frame3->Scrollbar(-command => ['yview', $results]);
|
||||
$results->configure(-yscrollcommand => ['set', $scrollbar]);
|
||||
$scrollbar->pack(-side => 'right', -fill => 'y');
|
||||
$results->pack(-side => 'left', -expand => 'yes', -fill => 'both');
|
||||
|
||||
my $frame4 = $top->Frame(-borderwidth => '2m');
|
||||
$frame4->pack(-fill => 'x');
|
||||
|
||||
# Quit button
|
||||
my $quit = $frame4->Button(-text => 'Quit', -command => sub {exit 0;});
|
||||
$quit->pack(-side => 'bottom', -fill => 'x');
|
||||
#TODO: a "clear results" button and a "shrink results"
|
||||
|
||||
@main::to_disable = ($entry, $doit, $quit, $number, $size, $delay);
|
||||
@main::to_mark = ($label, $frame1, $frame2, $frame3, $frame4);
|
||||
|
||||
#TODO: better resizing: the Quit button disappears when shrinking
|
||||
|
||||
MainLoop;
|
||||
|
||||
sub doit {
|
||||
my ($top_window, $entry, $text, $number, $size, $delay, $label) = @_;
|
||||
my ($date) = `date`;
|
||||
my $line;
|
||||
my $index;
|
||||
chop $date;
|
||||
my $host = $entry->get;
|
||||
&disable (@main::to_disable);
|
||||
&mark_used (@main::to_mark);
|
||||
&status ($label, "Looking up $host");
|
||||
$label->update;
|
||||
my ($name, $aliases, $addrtype, $length, @addrs) = gethostbyname ($host);
|
||||
if (! $name) {
|
||||
$text->configure (-state => 'normal');
|
||||
$text->insert ('end', "\n----------\nHost $host unknown at $date\n\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
&status ($label, "Idle");
|
||||
&enable (@main::to_disable);
|
||||
&mark_unused (@main::to_mark);
|
||||
return;
|
||||
}
|
||||
my $address = join ('.', unpack('C4', $addrs[0]));
|
||||
&status ($label, "Echopinging $name");
|
||||
open (HANDLE, "$main::echoping -v -n $number -s $size -w $delay $address 2>&1 |") ||
|
||||
&panic ("Cannot echoping");
|
||||
$main::handle = *HANDLE;
|
||||
$top_window->fileevent ("HANDLE", 'readable', [\&message_from_echoping,
|
||||
$top_window, $text, \*HANDLE]);
|
||||
$text->configure (-state => 'normal');
|
||||
$text->insert ('end', "\n----------\n$main::echoping of $host ($name [" .
|
||||
$address . "])\n" .
|
||||
" (with $size bytes and $delay s interval)\n" .
|
||||
" at $date:\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
}
|
||||
|
||||
sub message_from_echoping {
|
||||
my ($top_window, $text, $handle) = @_;
|
||||
my ($line) = scalar <$handle>;
|
||||
if (! defined ($line)) {
|
||||
$top_window->fileevent ($handle, 'readable', "");
|
||||
close ($handle);
|
||||
&end_of_echoping;
|
||||
return;
|
||||
}
|
||||
chop $line;
|
||||
#TODO: cancel will only be taken into account when there is something to read :-(
|
||||
# may be more feedback would be good?
|
||||
$text->configure (-state => 'normal');
|
||||
if ($main::cancel_requested) {
|
||||
&cancel_requested ($top_window, $text);
|
||||
# The only problem is that we lose the last line received
|
||||
# but the test is here to have more opportunities to
|
||||
# catch a cancel.
|
||||
}
|
||||
elsif ($line =~ /^This is /) {
|
||||
&status ($main::text, "Trying to connect");
|
||||
}
|
||||
elsif ($line =~ /^Trying to connect to internet address/) {
|
||||
&status ($main::text, "Trying to connect");
|
||||
}
|
||||
elsif ($line =~ /^Connected/) {
|
||||
&status ($main::text, "Connected");
|
||||
}
|
||||
elsif ($line =~ /^Sent/) {
|
||||
&status ($main::text, "Data sent");
|
||||
}
|
||||
elsif ($line =~ /^[0-9]+ bytes read/) {
|
||||
&status ($main::text, "Data sent");
|
||||
}
|
||||
elsif ($line =~ /^Checked/) {
|
||||
&status ($main::text, "Data received and checked");
|
||||
}
|
||||
elsif ($line =~ /^[a-z]+ time:/i) {
|
||||
&status ($main::text, "Sleeping");
|
||||
$text->insert ('end', $line . "\n");
|
||||
}
|
||||
elsif ($line =~ /^---/i) {
|
||||
&status ($main::text, "Sleeping");
|
||||
$text->insert ('end', $line . "\n");
|
||||
}
|
||||
elsif ($line =~ /^$/) {
|
||||
}
|
||||
else {
|
||||
&status ($main::text, "Strange value");
|
||||
$text->insert ('end', "Strange text: " . $line . "\n");
|
||||
}
|
||||
#$text->update;
|
||||
#TODO: scroll to see the end since it doesn't seem automatic
|
||||
$text->configure (-state => 'disabled');
|
||||
}
|
||||
|
||||
sub cancel_requested {
|
||||
my ($top_window, $text) = @_;
|
||||
$top_window->fileevent ($main::handle, 'readable', "");
|
||||
close ($main::handle);
|
||||
$text->configure (-state => 'normal');
|
||||
undef $main::cancel_requested;
|
||||
$text->insert ('end', "\nCancelled by user\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
&enable (@main::to_disable);
|
||||
&mark_unused (@main::to_mark);
|
||||
&status ($main::text, "Idle");
|
||||
}
|
||||
|
||||
sub end_of_echoping {
|
||||
my ($text, $line) = @_;
|
||||
&enable (@main::to_disable);
|
||||
&mark_unused (@main::to_mark);
|
||||
&status ($main::text, "Idle");
|
||||
}
|
||||
|
||||
sub status {
|
||||
my ($label, $message) = @_;
|
||||
$label->configure (-text=>"Status: $message");
|
||||
}
|
||||
|
||||
# Disable a list of widgets
|
||||
sub disable {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-state=>'disabled', -cursor=>'watch');
|
||||
}
|
||||
}
|
||||
|
||||
# Enable a list of widgets
|
||||
sub enable {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-state=>'normal', -cursor=>'top_left_arrow');
|
||||
}
|
||||
}
|
||||
|
||||
# Mark a list of widgets as used
|
||||
sub mark_used {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-cursor=>'watch');
|
||||
}
|
||||
}
|
||||
|
||||
# Mark a list of widgets as unused
|
||||
sub mark_unused {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-cursor=>'top_left_arrow');
|
||||
}
|
||||
}
|
||||
|
||||
# The "find_pg" (find program) code has been stolen from "aub"
|
||||
# and lightly adapted.
|
||||
sub find_pg {
|
||||
#
|
||||
# find_pg: find the specified executable on this machine, if possible.
|
||||
#
|
||||
# We try using which first, assuming that if the desired executable is in
|
||||
# our path, it's the one we want.
|
||||
#
|
||||
# If it's not in our path, we try whereis, returning the first program
|
||||
# whereis names for us which is executable.
|
||||
#
|
||||
# If we can't find what we need, we return an empty string.
|
||||
#
|
||||
# Bug: if the ".cshrc" of the user displays something, we're lost...
|
||||
|
||||
my ($pg) = @_;
|
||||
my ($ex) = 1;
|
||||
my ($try, @found);
|
||||
my ($pid);
|
||||
|
||||
return $pg if ($pg =~ m/^\//); # Absolute paths know best
|
||||
#chop($try = `which $pg`);
|
||||
die unless (defined ($pid = open(KID, "-|")));
|
||||
if ($pid) { # parent
|
||||
while (<KID>) {
|
||||
$try = $_;
|
||||
}
|
||||
chop $try;
|
||||
} else {
|
||||
#$> = $<;
|
||||
#$) = $(; # BUG: initgroups() not called
|
||||
exec '/usr/ucb/which', $pg;
|
||||
die "can't exec program: $!";
|
||||
}
|
||||
#print "\"", $try, "\"\n";
|
||||
if ($try =~ m#^/#) {
|
||||
#print "Try est absolu\n";
|
||||
}
|
||||
return $try if ($try =~ m#^/#);
|
||||
|
||||
chop($try = `whereis $pg`);
|
||||
if ($try =~ m/^$pg:\s+\//) {
|
||||
@found = split(/\s/, $try);
|
||||
$ex++ while (! -x $found[$ex]);
|
||||
return $found[$ex] unless ($found[$ex] eq "");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
#include "echoping.h"
|
||||
|
||||
|
||||
/* Most of error-handling routines stolen from Stevens' books */
|
||||
|
||||
void
|
||||
my_perror ()
|
||||
{
|
||||
fprintf (stderr, " %s\n", sys_err_str ());
|
||||
}
|
||||
|
||||
/*
|
||||
* Recoverable error. Print a message, and return to caller.
|
||||
*
|
||||
* err_ret(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
void
|
||||
err_ret (va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start (args);
|
||||
fmt = va_arg (args, char *);
|
||||
vfprintf (stderr, fmt, args);
|
||||
va_end (args);
|
||||
|
||||
my_perror ();
|
||||
|
||||
fflush (stdout);
|
||||
fflush (stderr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fatal error. Print a message and terminate. Don't dump core and don't
|
||||
* print the system's errno value.
|
||||
*
|
||||
* err_quit(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
void
|
||||
err_quit (va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start (args);
|
||||
fmt = va_arg (args, char *);
|
||||
vfprintf (stderr, fmt, args);
|
||||
fputc ('\n', stderr);
|
||||
va_end (args);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fatal error related to a system call. Print a message and terminate.
|
||||
* Don't dump core, but do print the system's errno value and its associated
|
||||
* message.
|
||||
*
|
||||
* err_sys(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
void
|
||||
err_sys (va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start (args);
|
||||
fmt = va_arg (args, char *);
|
||||
vfprintf (stderr, fmt, args);
|
||||
va_end (args);
|
||||
|
||||
my_perror ();
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void
|
||||
usage ()
|
||||
{
|
||||
fprintf (stderr, "Usage: %s [-v] [-t timeout] [-c] [-d] [-u] [-s size] [-n number] [-w delay] [-h url] server-name[:port]\n", progname);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a string containing some additional operating-system dependent
|
||||
* information. Note that different versions of UNIX assign different
|
||||
* meanings to the same value of "errno" (compare errno's starting with 35
|
||||
* between System V and BSD, for example). This means that if an error
|
||||
* condition is being sent to another UNIX system, we must interpret the
|
||||
* errno value on the system that generated the error, and not just send the
|
||||
* decimal value of errno to the other system.
|
||||
*/
|
||||
|
||||
char *
|
||||
sys_err_str ()
|
||||
{
|
||||
static char msgstr[200];
|
||||
|
||||
if (errno != 0)
|
||||
{
|
||||
if (errno > 0 && errno < sys_nerr)
|
||||
sprintf (msgstr, "(%s)", sys_errlist[errno]);
|
||||
else
|
||||
sprintf (msgstr, "(errno = %d)", errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgstr[0] = '\0';
|
||||
}
|
||||
|
||||
return (msgstr);
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
#ifdef HTTP
|
||||
|
||||
#include "echoping.h"
|
||||
#include "HTParse.h"
|
||||
|
||||
|
||||
char big_recvline[MAXTOREAD];
|
||||
|
||||
char *
|
||||
make_http_sendline (char *url, char *host, int port)
|
||||
{
|
||||
short sport = (short) port;
|
||||
int size = 200; /* Enough? */
|
||||
char *sendline = (char *) malloc (size);
|
||||
char *hostname = (char *) malloc (size);
|
||||
#ifdef HTTP10
|
||||
sprintf (sendline, "GET %s HTTP/1.0\r\nUser-Agent: Echoping/%s\r\n\r\n",
|
||||
url, VERSION);
|
||||
#else
|
||||
hostname = HTParse (url, "", PARSE_HOST);
|
||||
if (!strcmp (hostname, ""))
|
||||
sprintf (hostname, "%s:%d", host, sport);
|
||||
sprintf (sendline,
|
||||
"GET %s HTTP/1.1\r\nUser-Agent: Echoping/%s\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
||||
url, VERSION, hostname);
|
||||
#ifndef linux
|
||||
/* Bug in free(3)? */
|
||||
free (hostname);
|
||||
#endif
|
||||
#endif
|
||||
return sendline;
|
||||
}
|
||||
|
||||
void
|
||||
find_server_and_port (char *server, short *port, char *default_port)
|
||||
{
|
||||
char *text_port, *p;
|
||||
struct servent *sp;
|
||||
for (p = server; *p; p++)
|
||||
{
|
||||
if (*p == ':')
|
||||
{
|
||||
*p = 0;
|
||||
text_port = p + 1;
|
||||
*port = atoi (text_port);
|
||||
}
|
||||
}
|
||||
if (*port == 0)
|
||||
{
|
||||
if ((sp = getservbyname (default_port, "tcp")) == NULL)
|
||||
{
|
||||
err_quit ("tcp_open: unknown service: %s/tcp", default_port);
|
||||
}
|
||||
*port = sp->s_port;
|
||||
}
|
||||
else
|
||||
*port = htons (*port);
|
||||
}
|
||||
|
||||
int
|
||||
read_from_server (int fd)
|
||||
{
|
||||
int nr;
|
||||
int total = 0;
|
||||
char reply_code;
|
||||
int first_line = TRUE;
|
||||
short body = FALSE;
|
||||
while (!body)
|
||||
{
|
||||
nr = readline (fd, big_recvline, MAXTOREAD, TRUE);
|
||||
/* HTTP replies should be separated by CR-LF. Unfortunately, some
|
||||
servers send only CR :-( */
|
||||
body = ((nr == 2) || (nr == 1)); /* Empty line CR-LF seen */
|
||||
if ((nr < 1) && (errno == EINTR)) /* Probably a timeout */
|
||||
return -1;
|
||||
if (nr < 1)
|
||||
err_sys ("Error reading HTTP header");
|
||||
/* if ((int) big_recvline[nr-1] == 10)
|
||||
nr--; */
|
||||
if (first_line)
|
||||
{
|
||||
reply_code = big_recvline[9]; /* 9 because "HTTP/1.x 200..." */
|
||||
if (reply_code != '2') /* Status codes beginning with 3 are not errors
|
||||
but should never appear in reply to echoping's requests */
|
||||
err_quit ("HTTP error \"%s\"", big_recvline);
|
||||
}
|
||||
total = total + nr;
|
||||
first_line = FALSE;
|
||||
}
|
||||
/* Read the body */
|
||||
nr = readline (fd, big_recvline, MAXTOREAD, FALSE);
|
||||
if ((nr < 2) && (errno == EINTR)) /* Probably a timeout */
|
||||
return -1;
|
||||
if (nr < 2) /* Hmm, if the body is empty, we'll get a meaningless error message */
|
||||
err_sys ("Reading HTTP body");
|
||||
total = total + nr;
|
||||
return total; /* How to do if we want only the body's size? */
|
||||
}
|
||||
|
||||
#endif /* HTTP */
|
@ -0,0 +1,98 @@
|
||||
#ifdef ICP
|
||||
|
||||
/* Code contributed by Christian Grimm <grimm@rvs.uni-hannover.de>
|
||||
and patched by Stephane Bortzmeyer. */
|
||||
|
||||
#include "echoping.h"
|
||||
|
||||
void *
|
||||
make_icp_sendline (url, shost, opcode, length)
|
||||
|
||||
const char *url;
|
||||
u_num32 *shost;
|
||||
icp_opcode opcode;
|
||||
int *length;
|
||||
|
||||
{
|
||||
icp_common_t *headerp = NULL;
|
||||
char *buf = NULL;
|
||||
char *urloffset = NULL;
|
||||
u_num32 flags = ICP_FLAG_SRC_RTT;
|
||||
u_num32 pad = 0;
|
||||
u_num32 reqnum = 4711;
|
||||
unsigned short buf_len;
|
||||
|
||||
buf_len = sizeof (icp_common_t) + strlen (url) + 1;
|
||||
if (opcode == ICP_OP_QUERY)
|
||||
buf_len += sizeof (u_num32);
|
||||
buf = calloc (buf_len, 1);
|
||||
headerp = (icp_common_t *) buf;
|
||||
headerp->opcode = opcode;
|
||||
headerp->version = ICP_VERSION_CURRENT;
|
||||
headerp->length = htons (buf_len);
|
||||
headerp->reqnum = htonl (reqnum);
|
||||
headerp->flags = htonl (flags);
|
||||
headerp->pad = pad;
|
||||
headerp->shostid = htonl ((u_num32) shost);
|
||||
/* urloffset = (char *) ((int) buf + sizeof(icp_common_t)); */
|
||||
urloffset = (char *) (buf + sizeof (icp_common_t));
|
||||
if (opcode == ICP_OP_QUERY)
|
||||
urloffset += sizeof (u_num32);
|
||||
memcpy (urloffset, url, strlen (url));
|
||||
*length = buf_len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
int
|
||||
recv_icp (sockfd, buf, retcode)
|
||||
|
||||
int sockfd;
|
||||
char *buf;
|
||||
char *retcode;
|
||||
|
||||
{ /*
|
||||
* based on draft-wessels-icp-v2-02.txt
|
||||
*/
|
||||
icp_common_t *headerp = (icp_common_t *) buf;
|
||||
int nr, length;
|
||||
unsigned char opcode;
|
||||
static char *icp_op_code[] =
|
||||
{
|
||||
/* 0 */ "ICP_OP_INVALID",
|
||||
/* 1 */ "ICP_OP_QUERY",
|
||||
/* 2 */ "ICP_OP_HIT",
|
||||
/* 3 */ "ICP_OP_MISS",
|
||||
/* 4 */ "ICP_OP_ERR",
|
||||
/* 5 */ "",
|
||||
/* 6 */ "",
|
||||
/* 7 */ "",
|
||||
/* 8 */ "",
|
||||
/* 9 */ "",
|
||||
/* 10 */ "ICP_OP_SECHO",
|
||||
/* 11 */ "ICP_OP_DECHO",
|
||||
/* 12 */ "",
|
||||
/* 13 */ "",
|
||||
/* 14 */ "",
|
||||
/* 15 */ "",
|
||||
/* 16 */ "",
|
||||
/* 17 */ "",
|
||||
/* 18 */ "",
|
||||
/* 19 */ "",
|
||||
/* 20 */ "",
|
||||
/* 21 */ "ICP_OP_MISS_NOFETCH",
|
||||
/* 22 */ "ICP_OP_DENIED",
|
||||
/* 23 */ "ICP_OP_HIT_OBJ"
|
||||
};
|
||||
|
||||
|
||||
nr = recvfrom (sockfd, buf, DEFLINE, 0,
|
||||
(struct sockaddr *) 0, (int *) 0);
|
||||
/* BUG: we should test the return code, see if there was a timeout, etc */
|
||||
opcode = headerp->opcode;
|
||||
length = ntohs (headerp->length);
|
||||
sprintf (retcode, "ICP reply: \42%s\42", icp_op_code[opcode]);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ICP */
|
@ -0,0 +1,60 @@
|
||||
#ifndef ICP_HEADER
|
||||
|
||||
#define ICP_HEADER
|
||||
|
||||
/* Version */
|
||||
#define ICP_VERSION_1 1
|
||||
#define ICP_VERSION_2 2
|
||||
#define ICP_VERSION_3 3
|
||||
#define ICP_VERSION_CURRENT ICP_VERSION_2
|
||||
|
||||
#define ICP_FLAG_HIT_OBJ 0x80000000ul
|
||||
#define ICP_FLAG_SRC_RTT 0x40000000ul
|
||||
|
||||
#ifdef __alpha__
|
||||
typedef unsigned int u_num32;
|
||||
#else
|
||||
typedef unsigned long u_num32;
|
||||
#endif
|
||||
|
||||
struct icp_common_s {
|
||||
unsigned char opcode; /* opcode */
|
||||
unsigned char version; /* version number */
|
||||
unsigned short length; /* total length (bytes) */
|
||||
u_num32 reqnum; /* req number (req'd for UDP) */
|
||||
u_num32 flags;
|
||||
u_num32 pad;
|
||||
u_num32 shostid; /* sender host id */
|
||||
};
|
||||
typedef struct icp_common_s icp_common_t;
|
||||
|
||||
typedef enum {
|
||||
ICP_OP_INVALID, /* 00 to insure 0 doesn't get accidently interpreted. */
|
||||
ICP_OP_QUERY, /* 01 query opcode (cl->sv) */
|
||||
ICP_OP_HIT, /* 02 hit (cl<-sv) */
|
||||
ICP_OP_MISS, /* 03 miss (cl<-sv) */
|
||||
ICP_OP_ERR, /* 04 error (cl<-sv) */
|
||||
ICP_OP_SEND, /* 05 send object non-auth (cl->sv) */
|
||||
ICP_OP_SENDA, /* 06 send object authoritative (cl->sv) */
|
||||
ICP_OP_DATABEG, /* 07 first data, but not last (sv<-cl) */
|
||||
ICP_OP_DATA, /* 08 data middle of stream (sv<-cl) */
|
||||
ICP_OP_DATAEND, /* 09 last data (sv<-cl) */
|
||||
ICP_OP_SECHO, /* 10 echo from source (sv<-os) */
|
||||
ICP_OP_DECHO, /* 11 echo from dumb cache (sv<-dc) */
|
||||
ICP_OP_UNUSED0, /* 12 */
|
||||
ICP_OP_UNUSED1, /* 13 */
|
||||
ICP_OP_UNUSED2, /* 14 */
|
||||
ICP_OP_UNUSED3, /* 15 */
|
||||
ICP_OP_UNUSED4, /* 16 */
|
||||
ICP_OP_UNUSED5, /* 17 */
|
||||
ICP_OP_UNUSED6, /* 18 */
|
||||
ICP_OP_UNUSED7, /* 19 */
|
||||
ICP_OP_UNUSED8, /* 20 */
|
||||
ICP_OP_MISS_NOFETCH, /* 21 access denied while reloading */
|
||||
ICP_OP_DENIED, /* 22 access denied (cl<-sv) */
|
||||
ICP_OP_HIT_OBJ, /* 23 hit with object data (cl<-sv) */
|
||||
ICP_OP_END /* 24 marks end of opcodes */
|
||||
} icp_opcode;
|
||||
|
||||
|
||||
#endif /* ICP_HEADER */
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Read a line from a descriptor. Read the line one byte at a time, looking
|
||||
* for the newline. We store the newline in the buffer, then follow it with
|
||||
* a null (the same as fgets(3)). We return the number of characters up to,
|
||||
* but not including, the null (the same as strlen(3)). If ln == 0, we treat
|
||||
* newline as an ordinary charracter.
|
||||
*/
|
||||
|
||||
/* Stolen from Stevens' book */
|
||||
|
||||
#include "echoping.h"
|
||||
|
||||
int
|
||||
readline (fd, ptr, maxlen, ln)
|
||||
int fd;
|
||||
char *ptr;
|
||||
int maxlen;
|
||||
unsigned short ln;
|
||||
{
|
||||
int n, rc;
|
||||
char c;
|
||||
|
||||
for (n = 1; n < maxlen; n++)
|
||||
{
|
||||
if (timeout_flag)
|
||||
return n;
|
||||
if ((rc = read (fd, &c, 1)) == 1)
|
||||
{
|
||||
*ptr++ = c;
|
||||
if (c == '\n' && ln == 1)
|
||||
break;
|
||||
}
|
||||
else if (rc == 0)
|
||||
{
|
||||
if (n == 1)
|
||||
return (0); /* EOF, no data read */
|
||||
else
|
||||
break; /* EOF, some data was read */
|
||||
}
|
||||
else
|
||||
return (-1); /* error */
|
||||
}
|
||||
|
||||
*ptr = 0;
|
||||
return (n);
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Definitions for RTT timing.
|
||||
*/
|
||||
|
||||
#include "systype.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef BSD
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifdef SYS5
|
||||
#include <sys/times.h> /* requires <sys/types.h> */
|
||||
#include <sys/param.h> /* need the definition of HZ */
|
||||
#define TICKS HZ /* see times(2); usually 60 or 100 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Structure to contain everything needed for RTT timing.
|
||||
* One of these required per socket being timed.
|
||||
* The caller allocates this structure, then passes its address to
|
||||
* all the rtt_XXX() functions.
|
||||
*/
|
||||
|
||||
struct rtt_struct {
|
||||
float rtt_rtt; /* most recent round-trip time (RTT), seconds */
|
||||
float rtt_srtt; /* smoothed round-trip time (SRTT), seconds */
|
||||
float rtt_rttdev; /* smoothed mean deviation, seconds */
|
||||
short rtt_nrexmt; /* #times retransmitted: 0, 1, 2, ... */
|
||||
short rtt_currto; /* current retransmit timeout (RTO), seconds */
|
||||
short rtt_nxtrto; /* retransmit timeout for next packet, if nonzero */
|
||||
|
||||
#ifdef BSD
|
||||
struct timeval time_start; /* for elapsed time */
|
||||
struct timeval time_stop; /* for elapsed time */
|
||||
#endif
|
||||
|
||||
#ifdef SYS5
|
||||
long time_start; /* for elapsed time */
|
||||
long time_stop; /* for elapsed time */
|
||||
struct tms tms_start; /* arg to times(2), but not used */
|
||||
struct tms tms_stop; /* arg to times(2), but not used */
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#define RTT_RXTMIN 2 /* min retransmit timeout value, seconds */
|
||||
#define RTT_RXTMAX 120 /* max retransmit timeout value, seconds */
|
||||
#define RTT_MAXNREXMT 4 /* max #times to retransmit: must also
|
||||
change exp_backoff[] if this changes */
|
||||
|
||||
#ifdef SYS5
|
||||
long times(); /* the system call */
|
||||
#endif
|
||||
|
||||
extern int rtt_d_flag; /* can be set nonzero by caller for addl info */
|
@ -0,0 +1,33 @@
|
||||
CC = gcc
|
||||
#CC = cc
|
||||
CFLAGS = -c -O
|
||||
#CFLAGS = -g -c -O0
|
||||
LD = $(CC)
|
||||
LDFLAGS = -o echoping
|
||||
|
||||
OBJS = echoping.o error.o readline.o writen.o util.o
|
||||
SOURCES = echoping.c error.c readline.c writen.c util.c inet.h
|
||||
MISC = README Makefile
|
||||
DISTRIB= echo/README echo/Makefile echo/echoping.ptk echo/echoping.c echo/error.c echo/readline.c echo/writen.c echo/util.c echo/inet.h
|
||||
|
||||
all: echoping
|
||||
|
||||
echoping: $(OBJS)
|
||||
@ echo Linking $@ with new $?
|
||||
$(LD) $(LDFLAGS) $(OBJS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $<
|
||||
|
||||
clean:
|
||||
-rm echoping $(OBJS)
|
||||
@ echo Erased
|
||||
|
||||
distrib:
|
||||
@(cd .. ; \
|
||||
tar cvf "echo/echoping.tar" $(DISTRIB); \
|
||||
gzip -v -f "echo/echoping.tar")
|
||||
|
||||
checkout:
|
||||
co -l $(SOURCES) README
|
||||
|
@ -0,0 +1,120 @@
|
||||
"echoping" is a small program to test (approximatively) performances of a
|
||||
remote host by sending it TCP "echo" packets.
|
||||
|
||||
It assumes the remote host accepts such connections. Experience show that
|
||||
most Internet routers do and many hosts also. However, some Unices are not
|
||||
shipped with this service enabled and, anyway, the administrator is always
|
||||
free to close it (I think they shouldn't). echoping has therefore less chance
|
||||
to succeed than ping or bing. (On a typical Unix box, "echo" service is
|
||||
configured in /etc/inetd.conf.)
|
||||
|
||||
In any case, be polite: don't bother the remote host with many repeated
|
||||
requests, especially with large size.
|
||||
|
||||
The current version is very rough. It was written quickly and not debugged
|
||||
in detail.
|
||||
|
||||
It appears to compile and run at least on OSF/1 3.2, Solaris (?),
|
||||
Linux 1.1, SunOS 4.1 and Ultrix 4.3. You do not have to be root to
|
||||
install it: just type make and copy the "echoping" executable anywhere you
|
||||
want. There is no man page.
|
||||
|
||||
To use it, simply:
|
||||
|
||||
% echoping machine.somewhere.org
|
||||
|
||||
or use the options before the machine name:
|
||||
|
||||
-v : verbose
|
||||
-s nnn : size of the data to send
|
||||
-n nnn : numbers of repeated tests
|
||||
-w nnn : number of seconds to wait between two tests (default is one)
|
||||
-t nnn : number of seconds to wait a reply before giving up
|
||||
-u : use UDP instead of TCP
|
||||
-d : use the "discard" service instead of echo
|
||||
-c : use the "chargen" service instead of echo
|
||||
|
||||
echoping simply shows the elapsed time, including the time to set up the TCP
|
||||
connection and to transfer the data. Therefore, it is unsuitable to physical
|
||||
line raw throughput measures (unlike bing). On the other end, the action it
|
||||
performs are close from a HTTP request and it is meaningful to use it
|
||||
(carefully) to measure Web performances.
|
||||
|
||||
With the '-n' option, you have also the minimum, maximum, average and median
|
||||
time. The median is the value such that half of the measures are under it
|
||||
and the other half is above. When you measure highly variables values, like
|
||||
it is often the case on the whole Internet, median is better than average
|
||||
to avoid "extreme" values.
|
||||
|
||||
There are many, many traps when measuring something on the Internet. Just one
|
||||
example: 'echoping -w 0 -n 4 a-sunOS-machine' and you'll see the first test
|
||||
succeed in a very short time (if you are close from the machine) and all of
|
||||
the others take a much longer time (one second). With '-w 1' (wait one second
|
||||
between tests, the default), everything works fine: it seems the sockets on
|
||||
SunOS need time to recover :-)
|
||||
|
||||
With UDP servers you can have other surprises: the first test is quite often
|
||||
much slower since inetd has to launch the process. After that, the process
|
||||
stays a while so the next texts run faster.
|
||||
|
||||
If you have the Perl/Tk <http://pubweb.bnl.gov/~ptk/> package, you can use a
|
||||
(quite rough) windowing interface, "echoping.ptk". To use it, you should
|
||||
define FLUSH_OUTPUT at the beginning of echoping.c (this seems to work
|
||||
on only a few Unices, including DEC's OSF/1).
|
||||
|
||||
Known bugs:
|
||||
|
||||
- UDP isn't really useable with large packets because of sockets
|
||||
limitations and the lack of workaround code
|
||||
|
||||
To do:
|
||||
|
||||
- display statistics even when interrupted by Control-C
|
||||
- display other calculations such as standard deviation
|
||||
- timeouts even on TCP connections
|
||||
|
||||
To measure performances on the Internet you can also see:
|
||||
|
||||
Unix:
|
||||
|
||||
- bing, a bandwidth measurement tool <ftp://ftp.ibp.fr/pub/networking>
|
||||
- ping, probably available with your system
|
||||
- traceroute, idem (otherwise, see <ftp://ftp.ee.lbl.gov/>)
|
||||
- ttcp, the best measurement tool but it needs some control over the
|
||||
two machines <ftp://ftp.arl.mil/pub/ttcp>
|
||||
- spray is a tool which I dont't know very well. It is available on some
|
||||
machines (Sun, OSF/1).
|
||||
I've also heard of but never tried:
|
||||
- NetPerf <http://www.cup.hp.com/netperf/NetperfPage.html>
|
||||
- a suite of Bandwidth Measuring programs from gnn@netcom.com
|
||||
<ftp.netcom.com/~ftp/gnn/bwmeas-0.3.tar.Z>. These are several
|
||||
programs that measure bandwidth and jitter over several kinds of
|
||||
IPC links, including TCP and UDP.
|
||||
|
||||
Macintosh:
|
||||
|
||||
- TCP Watcher, a very nice "swiss-army knife" tool, to test ping, DNS, echo.
|
||||
It includes an echo server. Available on Info-Mac in "comm/tcp".
|
||||
|
||||
Web clients:
|
||||
|
||||
- You can ping or traceroute on the Web. See
|
||||
<http://hplyot.obspm.fr/cgi-bin/nph-traceroute>, <http://www.fr.net/>,
|
||||
and <gopher://ns.urec.fr/11/Reseaux/Annuaires>.
|
||||
|
||||
|
||||
Use all of them with care, the result is not obvious to interpret.
|
||||
|
||||
And don't forget to read RFC 1470 ("Tools for Monitoring and Debugging
|
||||
TCP/IP Internets and Interconnected Devices"), specially its "Benchmark"
|
||||
section and the Richard Stevens' books (all of them), published by
|
||||
Addison-Wesley.
|
||||
|
||||
|
||||
The reference site for echoping is:
|
||||
|
||||
ftp://ftp.pasteur.fr/pub/Network/echoping
|
||||
|
||||
Stephane Bortzmeyer <bortzmeyer@pasteur.fr>. October 1995 for the
|
||||
first version. December 1995 for this one.
|
||||
|
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* echoping : uses the TCP echo service to measure (roughly) response times.
|
||||
*
|
||||
* Written by Stephane Bortzmeyer <bortzmeyer@pasteur.fr>. A lot of code stolen
|
||||
* from Richard Stevens' book "Unix network programming" and Pierre Beyssac's
|
||||
* "bing" tool
|
||||
*
|
||||
*/
|
||||
|
||||
char *progname;
|
||||
unsigned short timeout_flag;
|
||||
|
||||
#include "inet.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
|
||||
|
||||
void
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
extern int opterr;
|
||||
extern int optopt;
|
||||
|
||||
extern int tvcmp();
|
||||
|
||||
char ch;
|
||||
|
||||
int sockfd;
|
||||
struct hostent *hostptr;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct sockaddr_in udp_cli_addr; /* client's Internet socket
|
||||
* addr */
|
||||
struct servent *sp;
|
||||
int verbose = FALSE;
|
||||
char *server_address;
|
||||
u_int addr;
|
||||
struct in_addr *ptr;
|
||||
int n, nr;
|
||||
char *sendline, recvline[MAXLINE + 1];
|
||||
struct timeval newtv, oldtv;
|
||||
|
||||
unsigned int size = DEFLINE;
|
||||
unsigned int number = 1;
|
||||
unsigned int wait = 1;
|
||||
unsigned char fill;
|
||||
unsigned short fill_requested = 0;
|
||||
unsigned int i, j = 0;
|
||||
unsigned int successes = 0;
|
||||
struct result {
|
||||
unsigned short valid;
|
||||
struct timeval timevalue;
|
||||
};
|
||||
struct result results[MAXNUMBER];
|
||||
struct timeval good_results[MAXNUMBER];
|
||||
|
||||
struct timeval max, min, total, median, temp;
|
||||
|
||||
void to_alarm(); /* our alarm() signal handler */
|
||||
void interrupted();
|
||||
unsigned int timeout = 10;
|
||||
unsigned short timeout_requested = 0;
|
||||
|
||||
char *port_name = ECHO_TCP_PORT;
|
||||
unsigned short port_to_use = USE_ECHO;
|
||||
unsigned short udp = 0;
|
||||
unsigned short ttcp = 0;
|
||||
|
||||
unsigned short stop_at_newlines = 1;
|
||||
|
||||
null_timeval.tv_sec = 0;
|
||||
null_timeval.tv_usec = 0;
|
||||
max_timeval.tv_sec = 1000000000;
|
||||
max_timeval.tv_usec = 999999;
|
||||
total = null_timeval;
|
||||
median = null_timeval;
|
||||
max = null_timeval;
|
||||
min = max_timeval;
|
||||
|
||||
for (i = 0; i <= MAXNUMBER; i++) {
|
||||
results[i].valid = 0;
|
||||
}
|
||||
progname = argv[0];
|
||||
while ((ch = getopt(argc, argv, "vs:n:w:dcrut:f:")) != EOF) {
|
||||
switch (ch) {
|
||||
case 'v':
|
||||
verbose = TRUE;
|
||||
break;
|
||||
case 'r':
|
||||
ttcp = 1;
|
||||
break;
|
||||
case 'u':
|
||||
udp = 1;
|
||||
break;
|
||||
case 'd':
|
||||
port_name = DISCARD_TCP_PORT;
|
||||
port_to_use = USE_DISCARD;
|
||||
break;
|
||||
case 'c':
|
||||
port_name = CHARACTER_GENERATOR_TCP_PORT;
|
||||
port_to_use = USE_CHARGEN;
|
||||
stop_at_newlines = 0;
|
||||
break;
|
||||
case 'f':
|
||||
fill = optarg;
|
||||
fill_requested = 1;
|
||||
break;
|
||||
case 's':
|
||||
size = atoi(optarg);
|
||||
if (size > MAXLINE) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: packet size too large, max is %d.\n",
|
||||
progname, MAXLINE);
|
||||
exit(1);
|
||||
}
|
||||
if (size <= 0) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: illegal packet size.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
timeout = atoi(optarg);
|
||||
timeout_requested = 1;
|
||||
if (size <= 0) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: illegal timeout.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
number = atoi(optarg);
|
||||
if (number > MAXNUMBER) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: number of iterations too large, max is %d.\n",
|
||||
progname, MAXNUMBER);
|
||||
exit(1);
|
||||
}
|
||||
if (number <= 0) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: illegal number of iterations.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
wait = atoi(optarg);
|
||||
if (wait < 0) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: illegal waiting time.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
if (udp && (port_to_use == USE_CHARGEN)) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: I don't know how to use CHARGEN with UDP.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
if (!udp && (timeout_requested)) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: Time out ignored for TCP connections.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
if (udp && ttcp) {
|
||||
(void) fprintf(stderr,
|
||||
"%s: UDP and T/TCP are incompatible.\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc != 1) {
|
||||
usage();
|
||||
}
|
||||
if (verbose) {
|
||||
printf("\nThis is %s, version %s.\n\n", progname, VERSION);
|
||||
}
|
||||
server = argv[0];
|
||||
signal(SIGINT, interrupted);
|
||||
if ((addr = inet_addr(server)) == INADDR_NONE) {
|
||||
if ((hostptr = gethostbyname(server)) == NULL) {
|
||||
err_quit("gethostbyname error for host: %s %s",
|
||||
server, sys_err_str());
|
||||
}
|
||||
server_address = *(hostptr->h_addr_list); /* First item of the
|
||||
* list */
|
||||
/*
|
||||
* addr = (u_long) *server_address; /* hostptr->h_addr_list
|
||||
* points actually to u_longs, not strings
|
||||
*/
|
||||
/* ptr.s_addr = addr; */
|
||||
ptr = (struct in_addr *) server_address; /* hostptr->h_addr_list
|
||||
* points actually to
|
||||
* u_longs, not strings */
|
||||
addr = ptr->s_addr;
|
||||
} else {
|
||||
ptr = (struct in_addr *) malloc(sizeof(struct in_addr));
|
||||
ptr->s_addr = addr;
|
||||
}
|
||||
if (!udp) {
|
||||
if ((sp = getservbyname(port_name, "tcp")) == NULL) {
|
||||
err_quit("tcp_open: unknown service: %s/tcp", port_name);
|
||||
}
|
||||
} else {
|
||||
if ((sp = getservbyname(port_name, "udp")) == NULL) {
|
||||
err_quit("tcp_open: unknown service: %s/udp", port_name);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Fill in the structure "serv_addr" with the address of the server
|
||||
* that we want to connect with.
|
||||
*/
|
||||
|
||||
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = addr;
|
||||
serv_addr.sin_port = sp->s_port;
|
||||
|
||||
if (!fill_requested) {
|
||||
sendline = random_string(size);
|
||||
} else {
|
||||
sendline = (char *) malloc(size);
|
||||
for (i = 0; i < size; i++)
|
||||
sendline[i] = fill;
|
||||
}
|
||||
n = strlen(sendline) + 1;
|
||||
|
||||
for (i = 1; i <= number; i++) {
|
||||
|
||||
if (!udp) {
|
||||
/*
|
||||
* Open a TCP socket (an Internet stream socket).
|
||||
*/
|
||||
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("Can't open stream socket");
|
||||
} else {
|
||||
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys("Can't open datagram socket");
|
||||
/* Bind socket for reply. Not necessary? */
|
||||
bzero((char *) &udp_cli_addr, sizeof(udp_cli_addr));
|
||||
udp_cli_addr.sin_family = AF_INET;
|
||||
udp_cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
udp_cli_addr.sin_port = htons(0);
|
||||
if (bind(sockfd, (struct sockaddr *) & udp_cli_addr,
|
||||
sizeof(udp_cli_addr)) < 0) {
|
||||
err_sys("bind error");
|
||||
}
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Trying to connect to internet address %s to transmit %u bytes...\n",
|
||||
inet_ntoa(*ptr), size);
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush((FILE *) NULL) != 0) {
|
||||
err_sys("I cannot flush");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
(void) gettimeofday(&oldtv, (struct timezone *) NULL);
|
||||
|
||||
if (!ttcp) {
|
||||
/*
|
||||
* Connect to the server.
|
||||
*/
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *) & serv_addr,
|
||||
sizeof(serv_addr)) < 0)
|
||||
err_sys("Can't connect to server");
|
||||
if (verbose) {
|
||||
printf("Connected...\n");
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush((FILE *) NULL) != 0) {
|
||||
err_sys("I cannot flush");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Not T/TCP */
|
||||
else {
|
||||
/* No initial connection */
|
||||
}
|
||||
if ((port_to_use == USE_ECHO) || (port_to_use == USE_DISCARD)) {
|
||||
if (ttcp) {
|
||||
if (sendto(sockfd, sendline, n, MSG_EOF,
|
||||
(struct sockaddr *) & serv_addr, sizeof(serv_addr)) != n)
|
||||
err_sys("sendto error on socket");
|
||||
if (verbose) {
|
||||
printf("T/TCP connection done\n");
|
||||
}
|
||||
} else if (!udp) {
|
||||
/* Write something to the server */
|
||||
if (writen(sockfd, sendline, n) != n)
|
||||
err_sys("writen error on socket");
|
||||
} else {
|
||||
/*
|
||||
* 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) {
|
||||
printf("Sent (%d bytes)...\n", n);
|
||||
#ifdef FLUSH_OUTPUT
|
||||
if (fflush((FILE *) NULL) != 0) {
|
||||
err_sys("I cannot flush");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if ((port_to_use == USE_ECHO) || (port_to_use == USE_CHARGEN)) {
|
||||
if (!udp) {
|
||||
/* Read from the server */
|
||||
nr = readline(sockfd, recvline, n, stop_at_newlines);
|
||||
} else {
|
||||
signal(SIGALRM, to_alarm);
|
||||
timeout_flag = 0; /* for signal handler */
|
||||
alarm(timeout);
|
||||
nr = recv(sockfd, recvline, n, 0);
|
||||
/*
|
||||
* nr = recvfrom(sockfd, recvline, n, 0,
|
||||
* (struct sockaddr *) 0, (int *) 0);
|
||||
* recvfrom fails on SunOS on connected
|
||||
* sockets.
|
||||
*/
|
||||
/*
|
||||
* BUG: 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
|
||||
}
|
||||
}
|
||||
if (nr < 0 || nr != n)
|
||||
err_sys("readline error: %d bytes read, %d bytes requested", nr, n);
|
||||
}
|
||||
/* That's all, folks */
|
||||
close(sockfd);
|
||||
|
||||
(void) gettimeofday(&newtv, (struct timezone *) NULL);
|
||||
temp = newtv;
|
||||
tvsub(&temp, &oldtv);
|
||||
if (!timeout_flag) {
|
||||
tvadd(&total, &temp);
|
||||
|
||||
/* Check */
|
||||
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) {
|
||||
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", newtv.tv_sec, 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) {
|
||||
sleep(wait);
|
||||
}
|
||||
}
|
||||
printstats:
|
||||
/* if ((number > 1) && ((!udp) || (successes > 0))) { */
|
||||
if (successes > 1) {
|
||||
printf("---\n");
|
||||
if (successes < number)
|
||||
printf("Warning: %d messages lost (%d %%)\n", number - successes,
|
||||
((number - successes) * 100) / number);
|
||||
printf("Minimum time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
||||
min.tv_sec, min.tv_usec, (double) size / tv2double(min));
|
||||
printf("Maximum time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
||||
max.tv_sec, max.tv_usec, (double) size / tv2double(max));
|
||||
tvavg(&total, successes);
|
||||
printf("Average time: %d.%06d seconds (%.0f bytes per sec.)\n",
|
||||
total.tv_sec, total.tv_usec, (double) size / tv2double(total));
|
||||
for (i = 0; i < number; i++) {
|
||||
if (results[i].valid)
|
||||
good_results[j++] = results[i].timevalue;
|
||||
}
|
||||
if (successes != j) /* 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",
|
||||
median.tv_sec, median.tv_usec, (double) size / tv2double(median));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 tout_flag flag.
|
||||
*/
|
||||
|
||||
void
|
||||
to_alarm()
|
||||
{
|
||||
timeout_flag = 1; /* set flag for function above */
|
||||
}
|
||||
|
||||
void
|
||||
interrupted()
|
||||
{
|
||||
printf("Interrupted by user\n");
|
||||
exit(1);
|
||||
}
|
@ -0,0 +1,314 @@
|
||||
#!/usr/local/bin/perl -w
|
||||
|
||||
require 5.001;
|
||||
use Tk;
|
||||
use Tk::IO;
|
||||
require 'sys/socket.ph';
|
||||
|
||||
# Let's be paranoid
|
||||
use strict;
|
||||
|
||||
require "newgetopt.pl";
|
||||
&NGetOpt (("geometry=s", "font=s", "background=s", "bg=s", "foreground=s", "fg=s", "title=s"));
|
||||
|
||||
my $default_host;
|
||||
if (@ARGV) {
|
||||
$default_host = shift (@ARGV);
|
||||
#TODO: shouldn't we try to echoping it right now?
|
||||
}
|
||||
else {
|
||||
$default_host = "localhost";
|
||||
}
|
||||
if (@ARGV) {
|
||||
print STDERR "Ignoring extra arguments \"" . join (' ', @ARGV) . "\"\n";
|
||||
}
|
||||
|
||||
my $top = MainWindow->new;
|
||||
|
||||
if ($main::opt_geometry) {
|
||||
$top->geometry ($main::opt_geometry);
|
||||
}
|
||||
if ($main::opt_title) {
|
||||
$top->title ($main::opt_title);
|
||||
}
|
||||
else {
|
||||
$top->title ("EchoPing Driver");
|
||||
}
|
||||
#TODO: how to set background, font, etc for all the widgets?
|
||||
if ($main::opt_bg) {
|
||||
$main::opt_background = $main::opt_bg;
|
||||
}
|
||||
if ($main::opt_background) {
|
||||
$top->configure (-background => $main::opt_background);
|
||||
}
|
||||
if ($main::opt_fg) {
|
||||
$main::opt_foreground = $main::opt_fg;
|
||||
}
|
||||
if ($main::opt_foreground) {
|
||||
$top->configure (-foreground => $main::opt_foreground);
|
||||
}
|
||||
if ($main::opt_font) {
|
||||
$top->configure (-font => $main::opt_font);
|
||||
}
|
||||
|
||||
#TODO : on line help with context => 'connection refused' will give an explanation
|
||||
|
||||
$main::echoping = &find_pg ("echoping");
|
||||
if (! $main::echoping) {
|
||||
print STDERR "Cannot find the echoping program in the path.\n";
|
||||
exit 1;
|
||||
#TODO: a nice pop-up window with an hypertext link to the FTP server :-)
|
||||
}
|
||||
|
||||
my $message;
|
||||
open (ECHOPING, "$main::echoping -v localhost 2>&1 |") || &panic ("Cannot echoping");
|
||||
my $result = <ECHOPING>;
|
||||
chop $result;
|
||||
if ($result) { # Something was wrong
|
||||
if ($result =~ /Connection refused/) {
|
||||
$message = "localhost refused echo: egoist!";
|
||||
#TODO: better explanations
|
||||
}
|
||||
else {
|
||||
$message = "Problem localhost: $result";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = <ECHOPING>;
|
||||
}
|
||||
close (ECHOPING);
|
||||
|
||||
# Some useful declarations
|
||||
my $text;
|
||||
my $results;
|
||||
my $number;
|
||||
my $size;
|
||||
my $delay;
|
||||
|
||||
my $frame1 = $top->Frame(-borderwidth => '2m');
|
||||
$frame1->pack(-fill => 'x');
|
||||
|
||||
# Entry field
|
||||
my $entry = $frame1->Entry(-relief => 'sunken', -width => 45);
|
||||
my $label = $frame1->Label(-text => 'Enter host name');
|
||||
$label->pack(-side => 'left');
|
||||
$entry->pack(-side => 'left');
|
||||
$entry->insert('insert', $default_host);
|
||||
$entry->selection ('range', 0, length ($default_host));
|
||||
$entry->focus;
|
||||
# I believe the following binding is necessary only on OSF/1?
|
||||
$entry->bind('<Delete>' => 'Backspace');
|
||||
|
||||
# Doit button
|
||||
my $doit = $frame1->Button(-text => 'Do it',
|
||||
-command => sub {doit ($top, $entry, $results, $number->get, $size->get, $delay->get, $text)});
|
||||
$doit->pack(-side => 'left', -fill => 'x', -padx => '2m');
|
||||
$top->bind ('<Return>' => sub {doit ($top, $entry, $results, $number->get, $size->get, $delay->get, $text)});
|
||||
my $cancel = $frame1->Button(-text => 'Cancel',
|
||||
-command => sub {$main::cancel_requested = 1;});
|
||||
#TODO: Cancel should test if an operation is in progress, otherwise, it will
|
||||
# be "recorded" for the next time.
|
||||
$cancel->pack(-side => 'left', -fill => 'x', -padx => '2m');
|
||||
|
||||
my $frame2 = $top->Frame(-borderwidth => '2m');
|
||||
$frame2->pack(-fill => 'x');
|
||||
#TODO: every number should be in the settings section at the beginning
|
||||
$number = $frame2->Scale(-from => '1', -to => '10', -orient => 'horizontal', -label => 'Number of connections');
|
||||
$number->set ('1');
|
||||
$number->pack (-side => 'top', -fill => 'x');
|
||||
$size = $frame2->Scale(-from => '1', -to => '1000', '-length' => '500', -orient => 'horizontal', -label => 'Size of packets');
|
||||
$size->set ('256'); #TODO: finds a way to enter value directly
|
||||
$size->pack (-side => 'top', -fill => 'x');
|
||||
$delay = $frame2->Scale(-from => '0', -to => '20', '-length' => '500', -orient => 'horizontal', -label => 'Delay between connections');
|
||||
$delay->set ('1');
|
||||
$delay->pack (-side => 'top', -fill => 'x');
|
||||
|
||||
my $frame3 = $top->Frame(-borderwidth => '2m');
|
||||
$frame3->pack (-fill => 'both', -expand => 'yes');
|
||||
|
||||
# Status text
|
||||
$text = $frame3->Label(
|
||||
-justify => 'center',
|
||||
-text => "$message",
|
||||
);
|
||||
$text->pack(-side => 'top', -fill => 'none', -expand => 'no');
|
||||
|
||||
# Results text with scrollbar
|
||||
#TODO: nice tags and hypertext tags
|
||||
$results = $frame3->Text(-relief => 'sunken', -state => 'disabled');
|
||||
my $scrollbar = $frame3->Scrollbar(-command => ['yview', $results]);
|
||||
$results->configure(-yscrollcommand => ['set', $scrollbar]);
|
||||
$scrollbar->pack(-side => 'right', -fill => 'y');
|
||||
$results->pack(-side => 'left', -expand => 'yes', -fill => 'both');
|
||||
|
||||
my $frame4 = $top->Frame(-borderwidth => '2m');
|
||||
$frame4->pack(-fill => 'x');
|
||||
|
||||
# Quit button
|
||||
my $quit = $frame4->Button(-text => 'Quit', -command => sub {exit 0;});
|
||||
$quit->pack(-side => 'bottom', -fill => 'x');
|
||||
#TODO: a "clear results" button and a "shrink results"
|
||||
|
||||
@main::to_disable = ($entry, $doit, $quit, $number, $size, $delay);
|
||||
@main::to_mark = ($label, $frame1, $frame2, $frame3, $frame4);
|
||||
|
||||
#TODO: better resizing: the Quit button disappears when shrinking
|
||||
|
||||
MainLoop;
|
||||
|
||||
sub doit {
|
||||
my ($top_window, $entry, $text, $number, $size, $delay, $label) = @_;
|
||||
my ($date) = `date`;
|
||||
my $line;
|
||||
my $index;
|
||||
chop $date;
|
||||
my $host = $entry->get;
|
||||
&disable (@main::to_disable);
|
||||
&mark_used (@main::to_mark);
|
||||
&status ($label, "Looking up $host");
|
||||
$label->update;
|
||||
my ($name, $aliases, $addrtype, $length, @addrs) = gethostbyname ($host);
|
||||
if (! $name) {
|
||||
$text->configure (-state => 'normal');
|
||||
$text->insert ('end', "\n----------\nHost $host unknown at $date\n\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
&status ($label, "Idle");
|
||||
&enable (@main::to_disable);
|
||||
&mark_unused (@main::to_mark);
|
||||
return;
|
||||
}
|
||||
my $address = join ('.', unpack('C4', $addrs[0]));
|
||||
&status ($label, "Echopinging $name");
|
||||
my $handle = Tk::IO->open ("$main::echoping -v -n $number -s $size -w $delay $address 2>&1 |") || &panic ("Cannot echoping");
|
||||
# Some messages to ignore. May be we should check them?
|
||||
my $garbage = $handle->readline;
|
||||
$garbage = $handle->readline;
|
||||
$garbage = $handle->readline;
|
||||
$text->configure (-state => 'normal');
|
||||
$text->insert ('end', "\n----------\n$main::echoping of $host ($name [" .
|
||||
$address . "])\n" .
|
||||
" (with $size bytes and $delay s interval)\n" .
|
||||
" at $date:\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
while ($line = $handle->readline) {
|
||||
#TODO: cancel will only be taken into account when there is something to read :-(
|
||||
# may be more feedback would be good?
|
||||
if ($main::cancel_requested) {
|
||||
last; # The only problem is that we lose the last line received
|
||||
# but the test is here to have more opportunities to
|
||||
# catch a cancel.
|
||||
}
|
||||
if ($line =~ /^Trying to connect to internet address/) {
|
||||
&status ($label, "Trying to connect");
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^Connected/) {
|
||||
&status ($label, "Connected");
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^Sent/) {
|
||||
&status ($label, "Data sent");
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^Checked/) {
|
||||
&status ($label, "Data received and checked");
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^Elapsed time/) {
|
||||
&status ($label, "Sleeping");
|
||||
}
|
||||
$text->configure (-state => 'normal');
|
||||
$text->insert ('end', $line);
|
||||
#$text->update;
|
||||
#TODO: scroll to see the end since it doesn't seem automatic
|
||||
$text->configure (-state => 'disabled');
|
||||
}
|
||||
$text->configure (-state => 'normal');
|
||||
if ($main::cancel_requested) {
|
||||
undef $main::cancel_requested;
|
||||
$text->insert ('end', "\nCancelled by user\n");
|
||||
}
|
||||
$handle->close;
|
||||
$date = `date`;
|
||||
chop $date;
|
||||
&status ($label, "Idle");
|
||||
$text->insert ('end', "Over at $date\n");
|
||||
$text->configure (-state => 'disabled');
|
||||
&enable (@main::to_disable);
|
||||
&mark_unused (@main::to_mark);
|
||||
}
|
||||
|
||||
sub status {
|
||||
my ($label, $message) = @_;
|
||||
$label->configure (-text=>"Status: $message");
|
||||
}
|
||||
|
||||
# Disable a list of widgets
|
||||
sub disable {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-state=>'disabled', -cursor=>'watch');
|
||||
}
|
||||
}
|
||||
|
||||
# Enable a list of widgets
|
||||
sub enable {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-state=>'normal', -cursor=>'top_left_arrow');
|
||||
}
|
||||
}
|
||||
|
||||
# Mark a list of widgets as used
|
||||
sub mark_used {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-cursor=>'watch');
|
||||
}
|
||||
}
|
||||
|
||||
# Mark a list of widgets as unused
|
||||
sub mark_unused {
|
||||
my (@widgets) = @_;
|
||||
my $w;
|
||||
for $w (@widgets) {
|
||||
$w->configure (-cursor=>'top_left_arrow');
|
||||
}
|
||||
}
|
||||
|
||||
# The "find_pg" (find program) code has been stolen from "aub"
|
||||
# and lightly adapted.
|
||||
sub find_pg {
|
||||
#
|
||||
# find_pg: find the specified executable on this machine, if possible.
|
||||
#
|
||||
# We try using which first, assuming that if the desired executable is in
|
||||
# our path, it's the one we want.
|
||||
#
|
||||
# If it's not in our path, we try whereis, returning the first program
|
||||
# whereis names for us which is executable.
|
||||
#
|
||||
# If we can't find what we need, we return an empty string.
|
||||
#
|
||||
|
||||
my ($pg) = @_;
|
||||
my ($ex) = 1;
|
||||
my ($try, @found);
|
||||
|
||||
return $pg if ($pg =~ m/^\//); # Absolute paths know best
|
||||
chop($try = `which $pg`);
|
||||
return $try if ($try =~ m/^\//);
|
||||
|
||||
chop($try = `whereis $pg`);
|
||||
if ($try =~ m/^$pg:\s+\//) {
|
||||
@found = split(/\s/, $try);
|
||||
$ex++ while (! -x $found[$ex]);
|
||||
return $found[$ex] unless ($found[$ex] eq "");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -0,0 +1,129 @@
|
||||
#include "inet.h"
|
||||
|
||||
|
||||
/* Most of error-handling routines stolen from Stevens' books */
|
||||
|
||||
/*
|
||||
* Recoverable error. Print a message, and return to caller.
|
||||
*
|
||||
* err_ret(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
err_ret(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start(args);
|
||||
fmt = va_arg(args, char *);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
my_perror();
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fatal error. Print a message and terminate. Don't dump core and don't
|
||||
* print the system's errno value.
|
||||
*
|
||||
* err_quit(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
err_quit(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start(args);
|
||||
fmt = va_arg(args, char *);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
va_end(args);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fatal error related to a system call. Print a message and terminate.
|
||||
* Don't dump core, but do print the system's errno value and its associated
|
||||
* message.
|
||||
*
|
||||
* err_sys(str, arg1, arg2, ...)
|
||||
*
|
||||
* The string "str" must specify the conversion specification for any args.
|
||||
*/
|
||||
|
||||
/* VARARGS1 */
|
||||
err_sys(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
char *fmt;
|
||||
|
||||
va_start(args);
|
||||
fmt = va_arg(args, char *);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
my_perror();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
my_perror()
|
||||
{
|
||||
fprintf(stderr, " %s\n", sys_err_str());
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-v] [-t timeout] [-c] [-d] [-u] [-s size] [-n number] [-w delay] server-name\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
extern int errno; /* Unix error number */
|
||||
extern int sys_nerr; /* # of error message strings in sys table */
|
||||
#ifdef __FreeBSD__
|
||||
#else
|
||||
extern char *sys_errlist[]; /* the system error message table */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return a string containing some additional operating-system dependent
|
||||
* information. Note that different versions of UNIX assign different
|
||||
* meanings to the same value of "errno" (compare errno's starting with 35
|
||||
* between System V and BSD, for example). This means that if an error
|
||||
* condition is being sent to another UNIX system, we must interpret the
|
||||
* errno value on the system that generated the error, and not just send the
|
||||
* decimal value of errno to the other system.
|
||||
*/
|
||||
|
||||
char *
|
||||
sys_err_str()
|
||||
{
|
||||
static char msgstr[200];
|
||||
|
||||
if (errno != 0) {
|
||||
if (errno > 0 && errno < sys_nerr)
|
||||
sprintf(msgstr, "(%s)", sys_errlist[errno]);
|
||||
else
|
||||
sprintf(msgstr, "(errno = %d)", errno);
|
||||
} else {
|
||||
msgstr[0] = '\0';
|
||||
}
|
||||
|
||||
return (msgstr);
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Definitions for TCP and UDP client/server programs.
|
||||
*/
|
||||
|
||||
#define VERSION "1.3.0-beta-T/TCP"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <varargs.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef SIGALRM /* Linux Slackware... */
|
||||
#define SIGALRM 14 /* alarm clock timeout */
|
||||
#endif
|
||||
#ifndef SIGINT /* Linux Slackware... */
|
||||
#define SIGINT 2 /* interrupt, generated from terminal special char */
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE (-1)
|
||||
#endif
|
||||
|
||||
struct timeval null_timeval;
|
||||
struct timeval max_timeval;
|
||||
|
||||
#define ECHO_TCP_PORT "echo"
|
||||
#define DISCARD_TCP_PORT "discard"
|
||||
#define CHARACTER_GENERATOR_TCP_PORT "chargen"
|
||||
|
||||
#define USE_ECHO 1
|
||||
#define USE_DISCARD 2
|
||||
#define USE_CHARGEN 3
|
||||
|
||||
#define CHARGENERATED " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg";
|
||||
|
||||
char *server;
|
||||
|
||||
char *sys_err_str();
|
||||
|
||||
char *random_string();
|
||||
|
||||
double tv2double();
|
||||
|
||||
#define DEFLINE 256
|
||||
#define MAXLINE 1500
|
||||
#define MAXNUMBER 20
|
||||
|
||||
extern char *progname;
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Read a line from a descriptor. Read the line one byte at a time, looking
|
||||
* for the newline. We store the newline in the buffer, then follow it with
|
||||
* a null (the same as fgets(3)). We return the number of characters up to,
|
||||
* but not including, the null (the same as strlen(3)). If ln == 0, we treat
|
||||
* newline as an ordinary charracter.
|
||||
*/
|
||||
|
||||
int
|
||||
readline(fd, ptr, maxlen, ln)
|
||||
int fd;
|
||||
char *ptr;
|
||||
int maxlen;
|
||||
unsigned short ln;
|
||||
{
|
||||
int n, rc;
|
||||
char c;
|
||||
|
||||
for (n = 1; n < maxlen; n++) {
|
||||
if ((rc = read(fd, &c, 1)) == 1) {
|
||||
*ptr++ = c;
|
||||
if (c == '\n' && ln == 1)
|
||||
break;
|
||||
} else if (rc == 0) {
|
||||
if (n == 1)
|
||||
return (0); /* EOF, no data read */
|
||||
else
|
||||
break; /* EOF, some data was read */
|
||||
} else
|
||||
return (-1); /* error */
|
||||
}
|
||||
|
||||
*ptr = 0;
|
||||
return (n);
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
# Change the following as required:
|
||||
CC = gcc
|
||||
|
||||
CFLAGS = -Wall
|
||||
#CFLAGS = -ansi -Wall -Dsun -D__STDC__=0 -DMSG_EOF=0
|
||||
# My flags for gcc/solaris 2.3: -ansi -Wall -Dsun -D__STDC__=0
|
||||
# (Solaris -DMSG_EOF just to let me compile it under Solaris; it won't run)
|
||||
# Add in -DGCC_STRUCT_PROBLEM for gcc versions 1.x under SunOS 4.x
|
||||
# Add in -D__STDC__=0 for gcc under Solaris 2 (for Sun's screwy headers)
|
||||
# Add in -D_BSD=44 for AIX 3.2.2 (see <sys/socket.h>)
|
||||
# Add in -D_SOCKADDR_LEN for DEC OSF/1 (see <sys/socket.h>)
|
||||
|
||||
# Following line for SVR4, Solaris 2.x; 2.5 no longer requires libucb.a.
|
||||
#LIBS = ./libmisc.a /usr/ucblib/libucb.a -lsocket -lnsl
|
||||
|
||||
# Following line for 4.4BSD, BSD/386, SunOS 4.x, AIX 3.2.2, OSF/1
|
||||
LIBS = ./libmisc.a
|
||||
|
||||
PROGS = udpcli udpserv tcpcli tcpserv ttcpcli ttcpserv \
|
||||
ttcpclibig ttcpservbig ttcphttpcli \
|
||||
udpclitime udpservtime tcpclitime tcpservtime ttcpclitime ttcpservtime
|
||||
|
||||
all: ${PROGS}
|
||||
|
||||
udpcli: udpcli.o
|
||||
${CC} ${CCFLAGS} -o $@ udpcli.o ${LIBS}
|
||||
|
||||
udpserv: udpserv.o
|
||||
${CC} ${CCFLAGS} -o $@ udpserv.o ${LIBS}
|
||||
|
||||
tcpcli: tcpcli.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ tcpcli.o readstream.o ${LIBS}
|
||||
|
||||
tcpserv: tcpserv.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ tcpserv.o readstream.o ${LIBS}
|
||||
|
||||
ttcpcli: ttcpcli.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpcli.o readstream.o ${LIBS}
|
||||
|
||||
ttcpserv: ttcpserv.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpserv.o readstream.o ${LIBS}
|
||||
|
||||
ttcpclibig: ttcpclibig.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpclibig.o readstream.o ${LIBS}
|
||||
|
||||
ttcpservbig: ttcpservbig.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpservbig.o readstream.o ${LIBS}
|
||||
|
||||
ttcphttpcli: ttcphttpcli.o readstream.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcphttpcli.o readstream.o ${LIBS}
|
||||
|
||||
udpclitime: udpclitime.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ udpclitime.o timer.o ${LIBS}
|
||||
|
||||
udpservtime: udpservtime.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ udpservtime.o timer.o ${LIBS}
|
||||
|
||||
tcpclitime: tcpclitime.o readstream.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ tcpclitime.o readstream.o timer.o ${LIBS}
|
||||
|
||||
tcpservtime: tcpservtime.o readstream.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ tcpservtime.o readstream.o timer.o ${LIBS}
|
||||
|
||||
ttcpclitime: ttcpclitime.o readstream.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpclitime.o readstream.o timer.o ${LIBS}
|
||||
|
||||
ttcpservtime: ttcpservtime.o readstream.o timer.o
|
||||
${CC} ${CCFLAGS} -o $@ ttcpservtime.o readstream.o timer.o ${LIBS}
|
||||
|
||||
clean:
|
||||
rm -f ${PROGS} core core.* *.o temp.* *.out typescript*
|
@ -0,0 +1,26 @@
|
||||
The files in this archive are the example programs from Chapter 1 of
|
||||
"TCP/IP Illustrated, Volume 3: TCP for Transactions, HTTP, NNTP, and
|
||||
the UNIX Domain Protocols" by W. Richard Stevens (Addison-Wesley, 1996).
|
||||
|
||||
All the source code files assume tabs stop every 4 columns, not 8. With
|
||||
vi I use ":set tabstop=4".
|
||||
|
||||
The files with "time" in their name are versions that can be used to time
|
||||
the client-server transactions (used for Figure 1.14 of the text). The
|
||||
three server programs with "time" in their name also allow a command-line
|
||||
option specifying how long they should sleep, to simulate the server
|
||||
processing time. Also notice that these "time" clients have a doubly
|
||||
nested loop: one for the request-reply size and another for the number
|
||||
of iterations.
|
||||
|
||||
The two T/TCP files with "big" in their name define the request and reply
|
||||
size to exceed one MSS (used for the example in Section 3.6 of the text).
|
||||
|
||||
*Before* trying to make these programs, do something like
|
||||
|
||||
cc -c error.c sleepus.c
|
||||
ar -crv libmisc.a error.o sleepus.o
|
||||
ranlib libmisc.a # needed for a BSD-derived system
|
||||
|
||||
Then change the definitions of CC, CFLAGS, and LIBS in the Makefile,
|
||||
as appropriate for your system. Then you can "make all".
|
@ -0,0 +1,32 @@
|
||||
/* Common includes and defines for UDP, TCP, and T/TCP
|
||||
* clients and servers */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define REQUEST 400 /* max size of request, in bytes */
|
||||
#define REPLY 400 /* max size of reply, in bytes */
|
||||
|
||||
#define UDP_SERV_PORT 7777 /* UDP server's well-known port */
|
||||
#define TCP_SERV_PORT 8888 /* TCP server's well-known port */
|
||||
#define TTCP_SERV_PORT 9999 /* T/TCP server's well-known port */
|
||||
|
||||
/* Following shortens all the type casts of pointer arguments */
|
||||
#define SA struct sockaddr *
|
||||
|
||||
void err_quit(const char *, ...);
|
||||
void err_sys(const char *, ...);
|
||||
int read_stream(int, char *, int);
|
||||
|
||||
/* following for timing versions of client-server */
|
||||
void start_timer(void);
|
||||
double print_timer(void);
|
||||
void sleep_us(unsigned int);
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include <errno.h> /* for definition of errno */
|
||||
#include <stdarg.h> /* ANSI C header file */
|
||||
#include <stdio.h> /* ANSI C header file */
|
||||
#include <stdlib.h> /* ANSI C header file */
|
||||
#include <string.h> /* ANSI C header file */
|
||||
#include <unistd.h> /* ANSI C header file */
|
||||
|
||||
#define MAXLINE 4096
|
||||
|
||||
static void err_doit(int, const char *, va_list);
|
||||
|
||||
/* Nonfatal error related to a system call.
|
||||
* Print a message and return. */
|
||||
|
||||
void
|
||||
err_ret(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(1, fmt, ap);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fatal error related to a system call.
|
||||
* Print a message and terminate. */
|
||||
|
||||
void
|
||||
err_sys(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(1, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Fatal error related to a system call.
|
||||
* Print a message, dump core, and terminate. */
|
||||
|
||||
void
|
||||
err_dump(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(1, fmt, ap);
|
||||
va_end(ap);
|
||||
abort(); /* dump core and terminate */
|
||||
exit(1); /* shouldn't get here */
|
||||
}
|
||||
|
||||
/* Nonfatal error unrelated to a system call.
|
||||
* Print a message and return. */
|
||||
|
||||
void
|
||||
err_msg(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(0, fmt, ap);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fatal error unrelated to a system call.
|
||||
* Print a message and terminate. */
|
||||
|
||||
void
|
||||
err_quit(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(0, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Print a message and return to caller.
|
||||
* Caller specifies "errnoflag". */
|
||||
|
||||
static void
|
||||
err_doit(int errnoflag, const char *fmt, va_list ap)
|
||||
{
|
||||
int errno_save;
|
||||
char buf[MAXLINE];
|
||||
|
||||
errno_save = errno; /* value caller might want printed */
|
||||
vsprintf(buf, fmt, ap);
|
||||
if (errnoflag)
|
||||
sprintf(buf+strlen(buf), ": %s", strerror(errno_save));
|
||||
strcat(buf, "\n");
|
||||
fflush(stdout); /* in case stdout and stderr are the same */
|
||||
fputs(buf, stderr);
|
||||
fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */
|
||||
return;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
read_stream(int fd, char *ptr, int maxbytes)
|
||||
{
|
||||
int nleft, nread;
|
||||
|
||||
nleft = maxbytes;
|
||||
while (nleft > 0) {
|
||||
if ( (nread = read(fd, ptr, nleft)) < 0)
|
||||
return(nread); /* error, return < 0 */
|
||||
else if (nread == 0)
|
||||
break; /* EOF, return #bytes read */
|
||||
nleft -= nread;
|
||||
ptr += nread;
|
||||
}
|
||||
return(maxbytes - nleft); /* return >= 0 */
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void
|
||||
sleep_us(unsigned int nusecs)
|
||||
{
|
||||
struct timeval tval;
|
||||
|
||||
for ( ; ; ) {
|
||||
tval.tv_sec = nusecs / 1000000;
|
||||
tval.tv_usec = nusecs % 1000000;
|
||||
if (select(0, NULL, NULL, NULL, &tval) == 0)
|
||||
break; /* all OK */
|
||||
/*
|
||||
* Note than on an interrupted system call (i.e, SIGIO) there's not
|
||||
* much we can do, since the timeval{} isn't updated with the time
|
||||
* remaining. We could obtain the clock time before the call, and
|
||||
* then obtain the clock time here, subtracting them to determine
|
||||
* how long select() blocked before it was interrupted, but that
|
||||
* seems like too much work :-)
|
||||
*/
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
err_sys("sleep_us: select error");
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) /* simple TCP client */
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int sockfd, n;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: tcpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
if (connect(sockfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("connect error");
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (write(sockfd, request, REQUEST) != REQUEST)
|
||||
err_sys("write error");
|
||||
if (shutdown(sockfd, 1) < 0)
|
||||
err_sys("shutdown error");
|
||||
|
||||
if ( (n = read_stream(sockfd, reply, REPLY)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[5000], reply[5000];
|
||||
int sockfd, n, len, i;
|
||||
double rtt, minrtt;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: tcpcli <IP address of server>");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
for (len = 0; len <= 1400; len += 100) {
|
||||
printf("len = %d\n", len);
|
||||
minrtt = 9999999;
|
||||
for (i = 0; i < 30; i++) {
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
start_timer();
|
||||
if (connect(sockfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("connect error");
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (write(sockfd, request, len) != len)
|
||||
err_sys("write error");
|
||||
if (shutdown(sockfd, 1) < 0)
|
||||
err_sys("shutdown error");
|
||||
|
||||
if ( (n = read_stream(sockfd, reply, len)) != len)
|
||||
err_sys("read error, n = %d", n);
|
||||
|
||||
rtt = print_timer();
|
||||
minrtt = min(minrtt, rtt);
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
close(sockfd);
|
||||
}
|
||||
printf("min rtt = %9.3f\n\n", minrtt);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main() /* simple TCP server */
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int listenfd, sockfd, n, clilen;
|
||||
|
||||
if ( (listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
if (bind(listenfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
if (listen(listenfd, SOMAXCONN) < 0)
|
||||
err_sys("listen error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (sockfd = accept(listenfd, (SA) &cli, &clilen)) < 0)
|
||||
err_sys("accept error");
|
||||
|
||||
if ( (n = read_stream(sockfd, request, REQUEST)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of request[] and create reply[] ... */
|
||||
|
||||
if (write(sockfd, reply, REPLY) != REPLY)
|
||||
err_sys("write error");
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[5000], reply[5000];
|
||||
int listenfd, sockfd, n, clilen, spt;
|
||||
|
||||
if (argc > 1)
|
||||
spt = atoi(argv[1]); /* optional server processing time, in ms */
|
||||
else
|
||||
spt = 0;
|
||||
|
||||
if ( (listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
if (bind(listenfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
if (listen(listenfd, SOMAXCONN) < 0)
|
||||
err_sys("listen error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (sockfd = accept(listenfd, (SA) &cli, &clilen)) < 0)
|
||||
err_sys("accept error");
|
||||
|
||||
if ( (n = read_stream(sockfd, request, sizeof(request))) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of request[] ... */
|
||||
if (spt)
|
||||
sleep_us(spt * 1000);
|
||||
|
||||
if (write(sockfd, reply, n) != n)
|
||||
err_sys("write error");
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
void tvsub(struct timeval *, struct timeval *);
|
||||
|
||||
struct timeval tvstart, tvend;
|
||||
|
||||
void
|
||||
start_timer()
|
||||
{
|
||||
if (gettimeofday(&tvstart, NULL) < 0)
|
||||
err_sys("start_timer: gettimeofday error");
|
||||
}
|
||||
|
||||
double
|
||||
print_timer()
|
||||
{
|
||||
double triptime;
|
||||
|
||||
if (gettimeofday(&tvend, NULL) < 0)
|
||||
err_sys("print_timer: gettimeofday error");
|
||||
|
||||
tvsub(&tvend, &tvstart);
|
||||
triptime = ((double) tvend.tv_sec) * 1000.0 +
|
||||
((double) tvend.tv_usec) / 1000.0; /* in millisec */
|
||||
printf("rtt = %9.3f ms\n", triptime);
|
||||
return(triptime);
|
||||
}
|
||||
|
||||
/* Subtract two timeval structures: *end -= *start */
|
||||
|
||||
void
|
||||
tvsub(struct timeval *end, struct timeval *start)
|
||||
{
|
||||
if ((end->tv_usec -= start->tv_usec) < 0) {
|
||||
--end->tv_sec;
|
||||
end->tv_usec += 1000000;
|
||||
}
|
||||
end->tv_sec -= start->tv_sec;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) /* T/TCP client */
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int sockfd, n;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: ttcpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (sendto(sockfd, request, REQUEST, MSG_EOF,
|
||||
(SA) &serv, sizeof(serv)) != REQUEST)
|
||||
err_sys("sendto error");
|
||||
|
||||
if ( (n = read_stream(sockfd, reply, REPLY)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#undef REQUEST
|
||||
#undef REPLY
|
||||
|
||||
#define REQUEST 3300 /* max size of request, in bytes */
|
||||
#define REPLY 3400 /* max size of reply, in bytes */
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int sockfd, n;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: ttcpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
n = 1;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_DEBUG,
|
||||
(char *) &n, sizeof(n)) < 0)
|
||||
err_sys("SO_DEBUG error");
|
||||
|
||||
#ifdef notdef
|
||||
n = 1;
|
||||
if (setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH,
|
||||
(char *) &n, sizeof(n)) < 0)
|
||||
err_sys("TCP_NOPUSH error");
|
||||
#endif
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (sendto(sockfd, request, REQUEST, MSG_EOF,
|
||||
(SA) &serv, sizeof(serv)) != REQUEST)
|
||||
err_sys("sendto error");
|
||||
|
||||
if ( (n = read_stream(sockfd, reply, REPLY)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[5000], reply[5000];
|
||||
int sockfd, n, len, i;
|
||||
double rtt, minrtt;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: ttcpcli <IP address of server>");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(TTCP_SERV_PORT);
|
||||
|
||||
for (len = 0; len <= 1400; len += 100) {
|
||||
printf("len = %d\n", len);
|
||||
minrtt = 9999999;
|
||||
for (i = 0; i < 30; i++) {
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
start_timer();
|
||||
if (sendto(sockfd, request, len, MSG_EOF,
|
||||
(SA) &serv, sizeof(serv)) != len)
|
||||
err_sys("sendto error");
|
||||
|
||||
if ( (n = read_stream(sockfd, reply, len)) < 0)
|
||||
err_sys("read error");
|
||||
rtt = print_timer();
|
||||
minrtt = min(minrtt, rtt);
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
close(sockfd);
|
||||
}
|
||||
printf("min rtt = %9.3f\n\n", minrtt);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) /* T/TCP client */
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char reply[REPLY];
|
||||
static char request[] = "GET / HTTP/1.0\n\r\n\r";
|
||||
int sockfd, n;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: ttcpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(80);
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (sendto(sockfd, request, sizeof(request)-1, MSG_EOF,
|
||||
(SA) &serv, sizeof(serv)) != sizeof(request)-1)
|
||||
err_sys("sendto error");
|
||||
|
||||
while ( (n = read_stream(sockfd, reply, REPLY)) > 0) {
|
||||
/* process "n" bytes of reply[] ... */
|
||||
write(1, reply, n);
|
||||
}
|
||||
if (n < 0)
|
||||
err_sys("read error");
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main() /* T/TCP server */
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int listenfd, sockfd, n, clilen;
|
||||
|
||||
if ( (listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
if (bind(listenfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
if (listen(listenfd, SOMAXCONN) < 0)
|
||||
err_sys("listen error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (sockfd = accept(listenfd, (SA) &cli, &clilen)) < 0)
|
||||
err_sys("accept error");
|
||||
|
||||
if ( (n = read_stream(sockfd, request, REQUEST)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of request[] and create reply[] ... */
|
||||
|
||||
if (send(sockfd, reply, REPLY, MSG_EOF) != REPLY)
|
||||
err_sys("send error");
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
#undef REQUEST
|
||||
#undef REPLY
|
||||
|
||||
#define REQUEST 3300 /* max size of request, in bytes */
|
||||
#define REPLY 3400 /* max size of reply, in bytes */
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int listenfd, sockfd, n, clilen;
|
||||
|
||||
if ( (listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(TCP_SERV_PORT);
|
||||
|
||||
if (bind(listenfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
if (listen(listenfd, SOMAXCONN) < 0)
|
||||
err_sys("listen error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (sockfd = accept(listenfd, (SA) &cli, &clilen)) < 0)
|
||||
err_sys("accept error");
|
||||
|
||||
if ( (n = read_stream(sockfd, request, REQUEST)) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of request[] ... */
|
||||
|
||||
if (sendto(sockfd, reply, REPLY, MSG_EOF, (SA) NULL, 0) != REPLY)
|
||||
err_sys("sendto error");
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[5000], reply[5000];
|
||||
int listenfd, sockfd, n, clilen, spt;
|
||||
|
||||
if (argc > 1)
|
||||
spt = atoi(argv[1]); /* optional server processing time, in ms */
|
||||
else
|
||||
spt = 0;
|
||||
|
||||
if ( (listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(TTCP_SERV_PORT);
|
||||
|
||||
if (bind(listenfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
if (listen(listenfd, SOMAXCONN) < 0)
|
||||
err_sys("listen error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (sockfd = accept(listenfd, (SA) &cli, &clilen)) < 0)
|
||||
err_sys("accept error");
|
||||
|
||||
if ( (n = read_stream(sockfd, request, sizeof(request))) < 0)
|
||||
err_sys("read error");
|
||||
|
||||
/* process "n" bytes of request[] ... */
|
||||
if (spt)
|
||||
sleep_us(spt * 1000);
|
||||
|
||||
if (sendto(sockfd, reply, n, MSG_EOF, (SA) NULL, 0) != n)
|
||||
err_sys("sendto error");
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) /* simple UDP client */
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int sockfd, n;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: udpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(UDP_SERV_PORT);
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
if (sendto(sockfd, request, REQUEST, 0,
|
||||
(SA) &serv, sizeof(serv)) != REQUEST)
|
||||
err_sys("sendto error");
|
||||
|
||||
if ( (n = recvfrom(sockfd, reply, REPLY, 0,
|
||||
(SA) NULL, (int *) NULL)) < 0)
|
||||
err_sys("recvfrom error");
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv;
|
||||
char request[5000], reply[5000];
|
||||
int sockfd, n, len, i;
|
||||
double rtt, minrtt;
|
||||
|
||||
if (argc != 2)
|
||||
err_quit("usage: udpcli <IP address of server>");
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = inet_addr(argv[1]);
|
||||
serv.sin_port = htons(UDP_SERV_PORT);
|
||||
|
||||
/* form request[] ... */
|
||||
|
||||
for (len = 0; len <= 1400; len += 100) { /* len = amount of user data */
|
||||
printf("len = %d\n", len);
|
||||
minrtt = 9999999;
|
||||
for (i = 0; i < 30; i++) {
|
||||
start_timer();
|
||||
|
||||
if (sendto(sockfd, request, len, 0,
|
||||
(SA) &serv, sizeof(serv)) != len)
|
||||
err_sys("sendto error");
|
||||
|
||||
if ( (n = recvfrom(sockfd, reply, sizeof(reply), 0,
|
||||
(SA) NULL, (int *) NULL)) != len)
|
||||
err_sys("recvfrom error");
|
||||
|
||||
rtt = print_timer();
|
||||
minrtt = min(minrtt, rtt);
|
||||
|
||||
/* process "n" bytes of reply[] ... */
|
||||
}
|
||||
printf("min rtt = %9.3f\n\n", minrtt);
|
||||
}
|
||||
exit(0);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main() /* simple UDP server */
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[REQUEST], reply[REPLY];
|
||||
int sockfd, n, clilen;
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, 0, sizeof(serv));
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(UDP_SERV_PORT);
|
||||
|
||||
if (bind(sockfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (n = recvfrom(sockfd, request, REQUEST, 0,
|
||||
(SA) &cli, &clilen)) < 0)
|
||||
err_sys("recvfrom error");
|
||||
|
||||
/* process "n" bytes of request[] and create reply[] ... */
|
||||
|
||||
if (sendto(sockfd, reply, REPLY, 0,
|
||||
(SA) &cli, sizeof(cli)) != REPLY)
|
||||
err_sys("sendto error");
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 1996 W. Richard Stevens. All rights reserved.
|
||||
* Permission to use or modify this software and its documentation only for
|
||||
* educational purposes and without fee is hereby granted, provided that
|
||||
* the above copyright notice appear in all copies. The author makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
#include "cliserv.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_in serv, cli;
|
||||
char request[5000], reply[5000];
|
||||
int sockfd, n, clilen, spt;
|
||||
|
||||
if (argc > 1)
|
||||
spt = atoi(argv[1]); /* optional server processing time, in ms */
|
||||
else
|
||||
spt = 0;
|
||||
|
||||
if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
err_sys("socket error");
|
||||
|
||||
memset(&serv, sizeof(serv), 0);
|
||||
serv.sin_family = AF_INET;
|
||||
serv.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv.sin_port = htons(UDP_SERV_PORT);
|
||||
|
||||
if (bind(sockfd, (SA) &serv, sizeof(serv)) < 0)
|
||||
err_sys("bind error");
|
||||
|
||||
for ( ; ; ) {
|
||||
clilen = sizeof(cli);
|
||||
if ( (n = recvfrom(sockfd, request, sizeof(request), 0,
|
||||
(SA) &cli, &clilen)) < 0)
|
||||
err_sys("recvfrom error");
|
||||
|
||||
/* process "n" bytes of request[] ... */
|
||||
if (spt)
|
||||
sleep_us(spt * 1000);
|
||||
|
||||
if (sendto(sockfd, reply, n, 0,
|
||||
(SA) &cli, sizeof(cli)) != n)
|
||||
err_sys("sendto error");
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
#include "inet.h"
|
||||
|
||||
#define STATES 32
|
||||
|
||||
#include <time.h>
|
||||
|
||||
char *
|
||||
random_string(unsigned length)
|
||||
{
|
||||
|
||||
char *state = (char *) malloc(sizeof(char) * STATES);
|
||||
char *result = (char *) malloc(length);
|
||||
int i, number;
|
||||
unsigned seed = (unsigned) time((time_t *) NULL);
|
||||
|
||||
/* printf ("Seed is %u\n", seed); */
|
||||
|
||||
/* Initialize random generator */
|
||||
(void) initstate(seed, state, STATES);
|
||||
|
||||
for (i = 0; i < (length - 1); i++) {
|
||||
number = (random() % 94) + 33;
|
||||
/* printf ("Number for %d is %d\n", i, number); */
|
||||
result[i] = (char) number;
|
||||
}
|
||||
result[length - 1] = '\0';
|
||||
|
||||
/* printf ("Result is %s\n", result); */
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* tvsub -- Subtract 2 timeval structs: out = out - in. Out is assumed to be
|
||||
* >= in. Comes from the bing program.
|
||||
*/
|
||||
void
|
||||
tvsub(out, in)
|
||||
struct timeval *out, *in;
|
||||
{
|
||||
if ((out->tv_usec -= in->tv_usec) < 0) {
|
||||
--out->tv_sec;
|
||||
out->tv_usec += 1000000;
|
||||
}
|
||||
out->tv_sec -= in->tv_sec;
|
||||
}
|
||||
|
||||
/* tvadd -- Adds 2 timeval structs: out = out + in. */
|
||||
void
|
||||
tvadd(out, in)
|
||||
struct timeval *out, *in;
|
||||
{
|
||||
if ((out->tv_usec += in->tv_usec) >= 1000000) {
|
||||
++out->tv_sec;
|
||||
out->tv_usec -= 1000000;
|
||||
}
|
||||
out->tv_sec += in->tv_sec;
|
||||
}
|
||||
|
||||
/* tvavg -- Averages a timeval struct */
|
||||
void
|
||||
tvavg(out, number)
|
||||
struct timeval *out;
|
||||
int number;
|
||||
{
|
||||
double result;
|
||||
/*
|
||||
* out->tv_sec = out->tv_sec/number; out->tv_usec =
|
||||
* out->tv_usec/number;
|
||||
*/
|
||||
result = (1000000 * out->tv_sec + out->tv_usec) / number;
|
||||
/* printf ("Result of average is %f\n", result) */ ;
|
||||
out->tv_sec = (long) (result / 1000000);
|
||||
out->tv_usec = (long) (result - (out->tv_sec * 1000000));
|
||||
}
|
||||
|
||||
/* tvcmp -- Compares two timeval structs */
|
||||
int
|
||||
tvcmp(left, right)
|
||||
struct timeval *left, *right;
|
||||
{
|
||||
if (left->tv_sec < right->tv_sec) {
|
||||
return -1;
|
||||
}
|
||||
if (left->tv_sec > right->tv_sec) {
|
||||
return 1;
|
||||
}
|
||||
if (left->tv_usec < right->tv_usec) {
|
||||
return -1;
|
||||
}
|
||||
if (left->tv_usec > right->tv_usec) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* tvmin */
|
||||
int
|
||||
tvmin(champion, challenger)
|
||||
struct timeval *champion, *challenger;
|
||||
{
|
||||
if (tvcmp(champion, challenger) == 1) {
|
||||
champion->tv_sec = challenger->tv_sec;
|
||||
champion->tv_usec = challenger->tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
/* tvmax */
|
||||
int
|
||||
tvmax(champion, challenger)
|
||||
struct timeval *champion, *challenger;
|
||||
{
|
||||
if (tvcmp(champion, challenger) == -1) {
|
||||
champion->tv_sec = challenger->tv_sec;
|
||||
champion->tv_usec = challenger->tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
tv2double(tv)
|
||||
struct timeval tv;
|
||||
{
|
||||
double result;
|
||||
result = (((((double) tv.tv_sec) * 1000000.0) + (double) tv.tv_usec) / 1000000.0);
|
||||
/* printf ("Double is %9.3f\n", result); */
|
||||
return result;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Write "n" bytes to a descriptor. Use in place of write() when fd is a
|
||||
* stream socket.
|
||||
*/
|
||||
|
||||
int
|
||||
writen(fd, ptr, nbytes)
|
||||
register int fd;
|
||||
register char *ptr;
|
||||
register int nbytes;
|
||||
{
|
||||
int nleft, nwritten;
|
||||
|
||||
nleft = nbytes;
|
||||
while (nleft > 0) {
|
||||
nwritten = write(fd, ptr, nleft);
|
||||
if (nwritten <= 0)
|
||||
return (nwritten); /* error */
|
||||
|
||||
nleft -= nwritten;
|
||||
ptr += nwritten;
|
||||
}
|
||||
return (nbytes - nleft);
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/* Most of it stolen from Pierre Beyssac's bing */
|
||||
|
||||
#include "echoping.h"
|
||||
|
||||
#define STATES 32
|
||||
|
||||
#include <time.h>
|
||||
|
||||
char *
|
||||
random_string (unsigned length)
|
||||
{
|
||||
|
||||
char *state = (char *) malloc (sizeof (char) * STATES);
|
||||
char *result = (char *) malloc (length);
|
||||
int i, number;
|
||||
unsigned seed = (unsigned) time ((time_t *) NULL);
|
||||
|
||||
/* printf ("Seed is %u\n", seed); */
|
||||
|
||||
/* Initialize random generator */
|
||||
(void) initstate (seed, state, STATES);
|
||||
|
||||
for (i = 0; i < (length - 1); i++)
|
||||
{
|
||||
number = (random () % 94) + 33;
|
||||
/* printf ("Number for %d is %d\n", i, number); */
|
||||
result[i] = (char) number;
|
||||
}
|
||||
result[length - 1] = '\0';
|
||||
|
||||
/* printf ("Result is %s\n", result); */
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* tvsub -- Subtract 2 timeval structs: out = out - in. Out is assumed to be
|
||||
* >= in. Comes from the bing program.
|
||||
*/
|
||||
void
|
||||
tvsub (out, in)
|
||||
struct timeval *out, *in;
|
||||
{
|
||||
if ((out->tv_usec -= in->tv_usec) < 0)
|
||||
{
|
||||
--out->tv_sec;
|
||||
out->tv_usec += 1000000;
|
||||
}
|
||||
out->tv_sec -= in->tv_sec;
|
||||
}
|
||||
|
||||
/* tvadd -- Adds 2 timeval structs: out = out + in. */
|
||||
void
|
||||
tvadd (out, in)
|
||||
struct timeval *out, *in;
|
||||
{
|
||||
if ((out->tv_usec += in->tv_usec) >= 1000000)
|
||||
{
|
||||
++out->tv_sec;
|
||||
out->tv_usec -= 1000000;
|
||||
}
|
||||
out->tv_sec += in->tv_sec;
|
||||
}
|
||||
|
||||
/* tvavg -- Averages a timeval struct */
|
||||
void
|
||||
tvavg (out, number)
|
||||
struct timeval *out;
|
||||
int number;
|
||||
{
|
||||
double result;
|
||||
/*
|
||||
* out->tv_sec = out->tv_sec/number; out->tv_usec =
|
||||
* out->tv_usec/number;
|
||||
*/
|
||||
result = (1000000 * out->tv_sec + out->tv_usec) / number;
|
||||
/* printf ("Result of average is %f\n", result) */ ;
|
||||
out->tv_sec = (long) (result / 1000000);
|
||||
out->tv_usec = (long) (result - (out->tv_sec * 1000000));
|
||||
}
|
||||
|
||||
/* tvcmp -- Compares two timeval structs */
|
||||
int
|
||||
tvcmp (left, right)
|
||||
struct timeval *left, *right;
|
||||
{
|
||||
if (left->tv_sec < right->tv_sec)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (left->tv_sec > right->tv_sec)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (left->tv_usec < right->tv_usec)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (left->tv_usec > right->tv_usec)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* tvmin */
|
||||
void
|
||||
tvmin (champion, challenger)
|
||||
struct timeval *champion, *challenger;
|
||||
{
|
||||
if (tvcmp (champion, challenger) == 1)
|
||||
{
|
||||
champion->tv_sec = challenger->tv_sec;
|
||||
champion->tv_usec = challenger->tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
/* tvmax */
|
||||
void
|
||||
tvmax (champion, challenger)
|
||||
struct timeval *champion, *challenger;
|
||||
{
|
||||
if (tvcmp (champion, challenger) == -1)
|
||||
{
|
||||
champion->tv_sec = challenger->tv_sec;
|
||||
champion->tv_usec = challenger->tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
tv2double (tv)
|
||||
struct timeval tv;
|
||||
{
|
||||
double result;
|
||||
result = (((((double) tv.tv_sec) * 1000000.0) + (double) tv.tv_usec) / 1000000.0);
|
||||
/* printf ("Double is %9.3f\n", result); */
|
||||
return result;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Write "n" bytes to a descriptor. Use in place of write() when fd is a
|
||||
* stream socket.
|
||||
*/
|
||||
|
||||
/* Stolen from Stevens' book */
|
||||
|
||||
#include "echoping.h"
|
||||
|
||||
int
|
||||
writen (fd, ptr, nbytes)
|
||||
register int fd;
|
||||
register char *ptr;
|
||||
register int nbytes;
|
||||
{
|
||||
int nleft, nwritten;
|
||||
|
||||
nleft = nbytes;
|
||||
while (nleft > 0)
|
||||
{
|
||||
nwritten = write (fd, ptr, nleft);
|
||||
if (nwritten <= 0)
|
||||
return (nwritten); /* error */
|
||||
/* Some systems, such as Digital's OSF1 (Digital Unix) doesn't set the returned
|
||||
value to -1, even when interrupted by an alarm, whatever says the documentation.
|
||||
errno is not set. */
|
||||
if ((nwritten < nleft) && timeout_flag)
|
||||
return nwritten;
|
||||
nleft -= nwritten;
|
||||
ptr += nwritten;
|
||||
}
|
||||
return (nbytes - nleft);
|
||||
}
|
Loading…
Reference in New Issue