From 40d676b53466c9b03d4f13b2096ed94867b27d16 Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Thu, 13 Apr 2000 09:19:23 +0000 Subject: [PATCH] Initial revision --- SRC/CHANGES | 22 + SRC/CREDITS | 10 + SRC/DETAILS | 98 ++++ SRC/HTParse.c | 719 ++++++++++++++++++++++++ SRC/HTParse.h | 98 ++++ SRC/INSTALL | 42 ++ SRC/MACHINES | 5 + SRC/Makefile | 76 +++ SRC/Makefile.am | 18 + SRC/README | 67 +++ SRC/TODO | 11 + SRC/echoping | Bin 0 -> 24890 bytes SRC/echoping.1 | 109 ++++ SRC/echoping.c | 834 ++++++++++++++++++++++++++++ SRC/echoping.h | 122 ++++ SRC/echoping.ptk | 365 ++++++++++++ SRC/error.c | 130 +++++ SRC/http.c | 100 ++++ SRC/icp.c | 98 ++++ SRC/icp.h | 60 ++ SRC/readline.c | 46 ++ SRC/rtt.h | 57 ++ SRC/ttcp/Makefile | 33 ++ SRC/ttcp/README | 120 ++++ SRC/ttcp/bidon.h | 0 SRC/ttcp/echoping.c | 475 ++++++++++++++++ SRC/ttcp/echoping.ptk | 314 +++++++++++ SRC/ttcp/error.c | 129 +++++ SRC/ttcp/inet.h | 61 ++ SRC/ttcp/readline.c | 35 ++ SRC/ttcp/ttcpcliserv/Makefile | 71 +++ SRC/ttcp/ttcpcliserv/README | 26 + SRC/ttcp/ttcpcliserv/cliserv.h | 32 ++ SRC/ttcp/ttcpcliserv/error.c | 110 ++++ SRC/ttcp/ttcpcliserv/readstream.c | 27 + SRC/ttcp/ttcpcliserv/sleepus.c | 37 ++ SRC/ttcp/ttcpcliserv/tcpcli.c | 46 ++ SRC/ttcp/ttcpcliserv/tcpclitime.c | 59 ++ SRC/ttcp/ttcpcliserv/tcpserv.c | 48 ++ SRC/ttcp/ttcpcliserv/tcpservtime.c | 55 ++ SRC/ttcp/ttcpcliserv/timer.c | 49 ++ SRC/ttcp/ttcpcliserv/ttcpcli.c | 42 ++ SRC/ttcp/ttcpcliserv/ttcpclibig.c | 61 ++ SRC/ttcp/ttcpcliserv/ttcpclitime.c | 54 ++ SRC/ttcp/ttcpcliserv/ttcphttpcli.c | 45 ++ SRC/ttcp/ttcpcliserv/ttcpserv.c | 48 ++ SRC/ttcp/ttcpcliserv/ttcpservbig.c | 54 ++ SRC/ttcp/ttcpcliserv/ttcpservtime.c | 55 ++ SRC/ttcp/ttcpcliserv/udpcli.c | 43 ++ SRC/ttcp/ttcpcliserv/udpclitime.c | 55 ++ SRC/ttcp/ttcpcliserv/udpserv.c | 42 ++ SRC/ttcp/ttcpcliserv/udpservtime.c | 49 ++ SRC/ttcp/util.c | 129 +++++ SRC/ttcp/writen.c | 24 + SRC/util.c | 140 +++++ SRC/writen.c | 33 ++ 56 files changed, 5688 insertions(+) create mode 100644 SRC/CHANGES create mode 100644 SRC/CREDITS create mode 100644 SRC/DETAILS create mode 100644 SRC/HTParse.c create mode 100644 SRC/HTParse.h create mode 100644 SRC/INSTALL create mode 100644 SRC/MACHINES create mode 100644 SRC/Makefile create mode 100644 SRC/Makefile.am create mode 100644 SRC/README create mode 100644 SRC/TODO create mode 100755 SRC/echoping create mode 100644 SRC/echoping.1 create mode 100644 SRC/echoping.c create mode 100644 SRC/echoping.h create mode 100755 SRC/echoping.ptk create mode 100644 SRC/error.c create mode 100644 SRC/http.c create mode 100644 SRC/icp.c create mode 100644 SRC/icp.h create mode 100644 SRC/readline.c create mode 100644 SRC/rtt.h create mode 100644 SRC/ttcp/Makefile create mode 100644 SRC/ttcp/README create mode 100644 SRC/ttcp/bidon.h create mode 100644 SRC/ttcp/echoping.c create mode 100755 SRC/ttcp/echoping.ptk create mode 100644 SRC/ttcp/error.c create mode 100644 SRC/ttcp/inet.h create mode 100644 SRC/ttcp/readline.c create mode 100644 SRC/ttcp/ttcpcliserv/Makefile create mode 100644 SRC/ttcp/ttcpcliserv/README create mode 100644 SRC/ttcp/ttcpcliserv/cliserv.h create mode 100644 SRC/ttcp/ttcpcliserv/error.c create mode 100644 SRC/ttcp/ttcpcliserv/readstream.c create mode 100644 SRC/ttcp/ttcpcliserv/sleepus.c create mode 100644 SRC/ttcp/ttcpcliserv/tcpcli.c create mode 100644 SRC/ttcp/ttcpcliserv/tcpclitime.c create mode 100644 SRC/ttcp/ttcpcliserv/tcpserv.c create mode 100644 SRC/ttcp/ttcpcliserv/tcpservtime.c create mode 100644 SRC/ttcp/ttcpcliserv/timer.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpcli.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpclibig.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpclitime.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcphttpcli.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpserv.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpservbig.c create mode 100644 SRC/ttcp/ttcpcliserv/ttcpservtime.c create mode 100644 SRC/ttcp/ttcpcliserv/udpcli.c create mode 100644 SRC/ttcp/ttcpcliserv/udpclitime.c create mode 100644 SRC/ttcp/ttcpcliserv/udpserv.c create mode 100644 SRC/ttcp/ttcpcliserv/udpservtime.c create mode 100644 SRC/ttcp/util.c create mode 100644 SRC/ttcp/writen.c create mode 100644 SRC/util.c create mode 100644 SRC/writen.c diff --git a/SRC/CHANGES b/SRC/CHANGES new file mode 100644 index 0000000..b3e8de8 --- /dev/null +++ b/SRC/CHANGES @@ -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. + diff --git a/SRC/CREDITS b/SRC/CREDITS new file mode 100644 index 0000000..17fb54b --- /dev/null +++ b/SRC/CREDITS @@ -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. + + diff --git a/SRC/DETAILS b/SRC/DETAILS new file mode 100644 index 0000000..061c96b --- /dev/null +++ b/SRC/DETAILS @@ -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 +.) + +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 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 +- ping, probably available with your system +- traceroute, idem (otherwise, see ) +- ttcp, the best measurement tool but it needs some control over the + two machines (nothing to do with + the T/TCP protocol) +- treno (evaluates available bandwidth for TCP) + +- 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 +- a suite of Bandwidth Measuring programs from gnn@netcom.com + . 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. + + +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 + or + . + + +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. diff --git a/SRC/HTParse.c b/SRC/HTParse.c new file mode 100644 index 0000000..832474f --- /dev/null +++ b/SRC/HTParse.c @@ -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; + } diff --git a/SRC/HTParse.h b/SRC/HTParse.h new file mode 100644 index 0000000..eb33efa --- /dev/null +++ b/SRC/HTParse.h @@ -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 + +#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 + */ diff --git a/SRC/INSTALL b/SRC/INSTALL new file mode 100644 index 0000000..63d2538 --- /dev/null +++ b/SRC/INSTALL @@ -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 + + +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"). + diff --git a/SRC/MACHINES b/SRC/MACHINES new file mode 100644 index 0000000..705b2fb --- /dev/null +++ b/SRC/MACHINES @@ -0,0 +1,5 @@ +www.obspm.fr +www.u-tokyo.ac.jp +www.fuw.edu.pl +www.fuw.edu.pl +www.kaist.ac.kr diff --git a/SRC/Makefile b/SRC/Makefile new file mode 100644 index 0000000..d712db4 --- /dev/null +++ b/SRC/Makefile @@ -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) diff --git a/SRC/Makefile.am b/SRC/Makefile.am new file mode 100644 index 0000000..d023394 --- /dev/null +++ b/SRC/Makefile.am @@ -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 + diff --git a/SRC/README b/SRC/README new file mode 100644 index 0000000..0eec6e6 --- /dev/null +++ b/SRC/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 . October 1995 for the +first version. August 1998 for this one. + diff --git a/SRC/TODO b/SRC/TODO new file mode 100644 index 0000000..4760b05 --- /dev/null +++ b/SRC/TODO @@ -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 diff --git a/SRC/echoping b/SRC/echoping new file mode 100755 index 0000000000000000000000000000000000000000..53b7f598c55a5fabf3276806b239a1600a67897f GIT binary patch literal 24890 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 + + diff --git a/SRC/echoping.c b/SRC/echoping.c new file mode 100644 index 0000000..c98e209 --- /dev/null +++ b/SRC/echoping.c @@ -0,0 +1,834 @@ +/* + * echoping : uses the TCP echo service to measure (roughly) response times. + * + * Written by Stephane Bortzmeyer . 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); +} diff --git a/SRC/echoping.h b/SRC/echoping.h new file mode 100644 index 0000000..49e13ed --- /dev/null +++ b/SRC/echoping.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; diff --git a/SRC/echoping.ptk b/SRC/echoping.ptk new file mode 100755 index 0000000..807dda8 --- /dev/null +++ b/SRC/echoping.ptk @@ -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 = ; +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 = ; +} +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('' => '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 ('' => 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 () { + $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 ""; +} + diff --git a/SRC/error.c b/SRC/error.c new file mode 100644 index 0000000..0fef26b --- /dev/null +++ b/SRC/error.c @@ -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); +} diff --git a/SRC/http.c b/SRC/http.c new file mode 100644 index 0000000..dfc3d70 --- /dev/null +++ b/SRC/http.c @@ -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 */ diff --git a/SRC/icp.c b/SRC/icp.c new file mode 100644 index 0000000..1a619a9 --- /dev/null +++ b/SRC/icp.c @@ -0,0 +1,98 @@ +#ifdef ICP + +/* Code contributed by Christian Grimm + 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 */ diff --git a/SRC/icp.h b/SRC/icp.h new file mode 100644 index 0000000..bfdf726 --- /dev/null +++ b/SRC/icp.h @@ -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 */ diff --git a/SRC/readline.c b/SRC/readline.c new file mode 100644 index 0000000..5e6280c --- /dev/null +++ b/SRC/readline.c @@ -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); +} diff --git a/SRC/rtt.h b/SRC/rtt.h new file mode 100644 index 0000000..1b79157 --- /dev/null +++ b/SRC/rtt.h @@ -0,0 +1,57 @@ +/* + * Definitions for RTT timing. + */ + +#include "systype.h" +#include + +#ifdef BSD +#include +#include +#endif + +#ifdef SYS5 +#include /* requires */ +#include /* 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 */ diff --git a/SRC/ttcp/Makefile b/SRC/ttcp/Makefile new file mode 100644 index 0000000..fa2602c --- /dev/null +++ b/SRC/ttcp/Makefile @@ -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 + diff --git a/SRC/ttcp/README b/SRC/ttcp/README new file mode 100644 index 0000000..6da96c2 --- /dev/null +++ b/SRC/ttcp/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 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 +- ping, probably available with your system +- traceroute, idem (otherwise, see ) +- ttcp, the best measurement tool but it needs some control over the + two machines +- 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 +- a suite of Bandwidth Measuring programs from gnn@netcom.com + . 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 + , , + and . + + +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 . October 1995 for the +first version. December 1995 for this one. + diff --git a/SRC/ttcp/bidon.h b/SRC/ttcp/bidon.h new file mode 100644 index 0000000..e69de29 diff --git a/SRC/ttcp/echoping.c b/SRC/ttcp/echoping.c new file mode 100644 index 0000000..6b01b53 --- /dev/null +++ b/SRC/ttcp/echoping.c @@ -0,0 +1,475 @@ +/* + * echoping : uses the TCP echo service to measure (roughly) response times. + * + * Written by Stephane Bortzmeyer . 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); +} diff --git a/SRC/ttcp/echoping.ptk b/SRC/ttcp/echoping.ptk new file mode 100755 index 0000000..edc8a66 --- /dev/null +++ b/SRC/ttcp/echoping.ptk @@ -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 = ; +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 = ; +} +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('' => '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 ('' => 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 ""; +} + diff --git a/SRC/ttcp/error.c b/SRC/ttcp/error.c new file mode 100644 index 0000000..634043a --- /dev/null +++ b/SRC/ttcp/error.c @@ -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); +} diff --git a/SRC/ttcp/inet.h b/SRC/ttcp/inet.h new file mode 100644 index 0000000..94f6338 --- /dev/null +++ b/SRC/ttcp/inet.h @@ -0,0 +1,61 @@ +/* + * Definitions for TCP and UDP client/server programs. + */ + +#define VERSION "1.3.0-beta-T/TCP" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; diff --git a/SRC/ttcp/readline.c b/SRC/ttcp/readline.c new file mode 100644 index 0000000..eac4c1d --- /dev/null +++ b/SRC/ttcp/readline.c @@ -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); +} diff --git a/SRC/ttcp/ttcpcliserv/Makefile b/SRC/ttcp/ttcpcliserv/Makefile new file mode 100644 index 0000000..08ee3ce --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/Makefile @@ -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 ) +# Add in -D_SOCKADDR_LEN for DEC OSF/1 (see ) + +# 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* diff --git a/SRC/ttcp/ttcpcliserv/README b/SRC/ttcp/ttcpcliserv/README new file mode 100644 index 0000000..401c361 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/README @@ -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". diff --git a/SRC/ttcp/ttcpcliserv/cliserv.h b/SRC/ttcp/ttcpcliserv/cliserv.h new file mode 100644 index 0000000..3631ef6 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/cliserv.h @@ -0,0 +1,32 @@ +/* Common includes and defines for UDP, TCP, and T/TCP + * clients and servers */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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)) diff --git a/SRC/ttcp/ttcpcliserv/error.c b/SRC/ttcp/ttcpcliserv/error.c new file mode 100644 index 0000000..1850d88 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/error.c @@ -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 /* for definition of errno */ +#include /* ANSI C header file */ +#include /* ANSI C header file */ +#include /* ANSI C header file */ +#include /* ANSI C header file */ +#include /* 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; +} diff --git a/SRC/ttcp/ttcpcliserv/readstream.c b/SRC/ttcp/ttcpcliserv/readstream.c new file mode 100644 index 0000000..b9514a6 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/readstream.c @@ -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 */ +} diff --git a/SRC/ttcp/ttcpcliserv/sleepus.c b/SRC/ttcp/ttcpcliserv/sleepus.c new file mode 100644 index 0000000..f38abaf --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/sleepus.c @@ -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 +#include +#include +#include + +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"); + } +} diff --git a/SRC/ttcp/ttcpcliserv/tcpcli.c b/SRC/ttcp/ttcpcliserv/tcpcli.c new file mode 100644 index 0000000..935a25f --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/tcpcli.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/tcpclitime.c b/SRC/ttcp/ttcpcliserv/tcpclitime.c new file mode 100644 index 0000000..cf950ee --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/tcpclitime.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/tcpserv.c b/SRC/ttcp/ttcpcliserv/tcpserv.c new file mode 100644 index 0000000..1bc26d0 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/tcpserv.c @@ -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); + } +} diff --git a/SRC/ttcp/ttcpcliserv/tcpservtime.c b/SRC/ttcp/ttcpcliserv/tcpservtime.c new file mode 100644 index 0000000..dd8703d --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/tcpservtime.c @@ -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); + } +} diff --git a/SRC/ttcp/ttcpcliserv/timer.c b/SRC/ttcp/ttcpcliserv/timer.c new file mode 100644 index 0000000..78c263f --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/timer.c @@ -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 + +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; +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpcli.c b/SRC/ttcp/ttcpcliserv/ttcpcli.c new file mode 100644 index 0000000..d859116 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpcli.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpclibig.c b/SRC/ttcp/ttcpcliserv/ttcpclibig.c new file mode 100644 index 0000000..732a656 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpclibig.c @@ -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 + +#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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpclitime.c b/SRC/ttcp/ttcpcliserv/ttcpclitime.c new file mode 100644 index 0000000..771df50 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpclitime.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/ttcphttpcli.c b/SRC/ttcp/ttcpcliserv/ttcphttpcli.c new file mode 100644 index 0000000..ab87ee1 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcphttpcli.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpserv.c b/SRC/ttcp/ttcpcliserv/ttcpserv.c new file mode 100644 index 0000000..c86a9be --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpserv.c @@ -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); + } +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpservbig.c b/SRC/ttcp/ttcpcliserv/ttcpservbig.c new file mode 100644 index 0000000..a03b9ed --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpservbig.c @@ -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); + } +} diff --git a/SRC/ttcp/ttcpcliserv/ttcpservtime.c b/SRC/ttcp/ttcpcliserv/ttcpservtime.c new file mode 100644 index 0000000..3e186b5 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/ttcpservtime.c @@ -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); + } +} diff --git a/SRC/ttcp/ttcpcliserv/udpcli.c b/SRC/ttcp/ttcpcliserv/udpcli.c new file mode 100644 index 0000000..7256804 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/udpcli.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/udpclitime.c b/SRC/ttcp/ttcpcliserv/udpclitime.c new file mode 100644 index 0000000..c6d8a0f --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/udpclitime.c @@ -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 "); + + 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); +} diff --git a/SRC/ttcp/ttcpcliserv/udpserv.c b/SRC/ttcp/ttcpcliserv/udpserv.c new file mode 100644 index 0000000..45d8452 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/udpserv.c @@ -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"); + } +} diff --git a/SRC/ttcp/ttcpcliserv/udpservtime.c b/SRC/ttcp/ttcpcliserv/udpservtime.c new file mode 100644 index 0000000..70b5f59 --- /dev/null +++ b/SRC/ttcp/ttcpcliserv/udpservtime.c @@ -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"); + } +} diff --git a/SRC/ttcp/util.c b/SRC/ttcp/util.c new file mode 100644 index 0000000..98f5e3a --- /dev/null +++ b/SRC/ttcp/util.c @@ -0,0 +1,129 @@ +#include "inet.h" + +#define STATES 32 + +#include + +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; +} diff --git a/SRC/ttcp/writen.c b/SRC/ttcp/writen.c new file mode 100644 index 0000000..a7ab536 --- /dev/null +++ b/SRC/ttcp/writen.c @@ -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); +} diff --git a/SRC/util.c b/SRC/util.c new file mode 100644 index 0000000..99e0411 --- /dev/null +++ b/SRC/util.c @@ -0,0 +1,140 @@ +/* Most of it stolen from Pierre Beyssac's bing */ + +#include "echoping.h" + +#define STATES 32 + +#include + +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; +} diff --git a/SRC/writen.c b/SRC/writen.c new file mode 100644 index 0000000..22f70d1 --- /dev/null +++ b/SRC/writen.c @@ -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); +}