Initial revision

original
Stephane Bortzmeyer 24 years ago
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…
Cancel
Save