nttcp-1.47/ 40755 310 310 0 7217362221 11123 5ustar elmarelmarnttcp-1.47/nttcp.c100644 310 310 156527 7217362026 12577 0ustar elmarelmar/* This code was written and is copyrighted 1996,1997,1998 by * Elmar Bartel * Institut fuer Informatik * Technische Universitaet Muenchen * bartel@informatik.tu-muenchen.de * * Permission to use, copy, modify and distribute this software * and its documentation for any purpose, except making money+, is * herby granted, provided that the above copyright notice and * this permission appears in all places, where this code is * referenced or used literally. * * Pieces and ideas present in this code come from older * versions of the program ttcp (Test-TCP), floating around * on the internet. * I cannot acknowledge the original author, simply because I found * no reference. If someone knows more about the history, let me * know. * * +To make it clear: * This statement does not prevent the packaging of this software * and takeing money for the packaging itself. It only means you * cannot make money from the program and its features. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for inet_ntoa, from where else? */ #include #include #include #include #if defined(SunOS53) #include int getrusage(int who, struct rusage *rusage); #endif /*SunOS53*/ #include "support.h" #if !defined(TTCP_SERVICE) #define TTCP_SERVICE 5037 #endif /*TTCP_SERVICE*/ #if !defined(DEFAULT_BUFCNT) #define DEFAULT_BUFCNT 2048 #endif /*DEFAULT_BUFCNT*/ #if !defined(DEFAULT_BUFLEN) #define DEFAULT_BUFLEN 4096 #endif /*DEFAULT_BUFLEN*/ #if !defined(NOBODY) #if defined(hpux) || defined(hpux9) || defined(hpux10) #if defined(hpux9) #define NOBODY 59999 #else #define NOBODY 60001 #endif /*hpux9*/ #else #define NOBODY (((unsigned)1<<(sizeof(uid_t)*8-1))-1) #endif /*hpux*/ #endif /*NOBODY*/ #if !defined(NTTCP_LOC_OPT) #define NTTCP_LOC_OPT "NTTCP_LOC_OPT" #endif /*NTTCP_LOC_OPT*/ #if !defined(NTTCP_REM_OPT) #define NTTCP_REM_OPT "NTTCP_REM_OPT" #endif /*NTTCP_REM_OPT*/ #if defined(IP_ADD_MEMBERSHIP) #define MULTICAST #else #undef MULTICAST #endif /* this is for multicast -- if the OS has support */ #if defined(MULTICAST) #if !defined(DEFAULT_CHANNEL) #define DEFAULT_CHANNEL "224.17.50.37" #endif /*DEFAULT_CHANNEL*/ #if !defined(DEFAULT_PORT) #define DEFAULT_PORT 5047 #endif /*DEFAULT_PORT*/ #endif /*MULTICAST*/ /* this is solely for debugging * normally it destroys the formatted output */ #define VERBOSE_CONNECT 2 /* architecture specific includes/defines */ /*====================================================================*/ /* what type to used for filedescriptor set's in select */ #define FdSet fd_set #define SELECT(n, rm, wm, em, to) \ select(n, (FdSet *)(rm), (FdSet *)(wm), (FdSet *)(em), to) #ifndef FD_COPY #define FD_COPY(src,dst) memcpy(dst, src, sizeof(*(dst))) #endif /*FD_COPY*/ /*====================================================================*/ #if defined(ultrix) #define openlog(ident, a, b) openlog(ident, 0) #endif /*ultrix*/ /*====================================================================*/ #if defined(hpux9) #include #define getrusage(a, b) syscall(SYS_GETRUSAGE, a, b) #undef SELECT #define SELECT(n, rm, wm, em, to) \ select(n, (int *)(rm), (int *)(wm), (int *)(em), to) void openlog(const char *ident, int logopt, int facility); void syslog(int priority, const char *message, ...); #endif /* hpux9 */ /*====================================================================*/ #if defined(aix) #undef FdSet #define FdSet void #endif /* aix */ /*====================================================================*/ #if defined(FreeBSD) #define SIGCLD SIGCHLD #endif /*FreeBSD*/ /*====================================================================*/ #if defined(BSD42) #define SetSockopt(s, lv, oname, oval, olen) \ setsockopt(s, lv, oname, 0, 0) #else #define SetSockopt(s, lv, oname, oval, olen) \ setsockopt(s, lv, oname, oval, olen) #endif /*BSD42*/ /*====================================================================*/ #if defined(SunOS4) #define NOSTRERROR #endif /*SunOS4*/ #if defined(NOSTRERROR) extern int sys_nerr; extern char *sys_errlist[]; char *strerror(int n) { static char WrongNumber[32]; if (0 <= n && n < sys_nerr) return sys_errlist[n]; else { sprintf(WrongNumber, "unknown errno: %d\n", n); return WrongNumber; } } #endif /*NOSTRERROR*/ /*====================================================================*/ #if defined(SunOS53) /* The following #defines address a VERY useful feature * the people of SunSoft introduced in SunOS5.3: nanoseconds. * They called their struct timespec - and as far as I * obseverved, they only use it in struct rusage. * The fucking thing: all other call's use the old timeval * with microseconds, so nothing is compatible anymore if you * do measuring with gettimeofday and getrusage. * I resolved this pitty, with using timespec allover * and adjusting the results of gettimeofday vi NANO_ADJ. * * Happy new Year, * Elmar Bartel, bartel@informatik.tu-muenchen.de * 29.12.1994 * * It's getting better all the time: they renamed it to "tv_nsec" * but deliver only values from 0..10^6-1 -- or say: I haven't * observed other values. Ok, we are prepared, but till now * we define SKIPOVER to 1000000. * When SunSoft decides to deliver real ns, simply define * SKIPOVER to 1000000000. * Despite this, have a happy new Year, * Elmar Bartel, bartel@informatik.tu-muenchen.de * 30.12.1994 */ #define TIMEVAL timespec #define SKIPOVER 1000000 #define FRACT tv_nsec #define FRACT_TO_SEC(tv) (((double)tv.tv_nsec)/SKIPOVER) #define FRACT_TO_DECI(tv) (tv.tv_nsec/(SKIPOVER/10)) #define FRACT_TO_CENT(tv) (tv.tv_nsec/(SKIPOVER/100)) #define FRACT_TO_MILL(tv) (tv.tv_nsec/(SKIPOVER/1000)) #define NANO_ADJ(tv) (tv).tv_nsec*= (SKIPOVER/1000000) #else /*SunOS53*/ #define TIMEVAL timeval #define SKIPOVER 1000000 #define FRACT tv_usec #define FRACT_TO_SEC(tv) (((double)tv.tv_usec)/SKIPOVER) #define FRACT_TO_DECI(tv) (tv.tv_usec/(SKIPOVER/10)) #define FRACT_TO_CENT(tv) (tv.tv_usec/(SKIPOVER/100)) #define FRACT_TO_MILL(tv) (tv.tv_usec/(SKIPOVER/1000)) #define NANO_ADJ(tv) #endif /*SunOS53*/ #define TimeVal struct TIMEVAL /*====================================================================*/ #if !defined(RUSAGE_SELF) /* definitions for systems without getrusage(2) calls, but times(2): * + SystemV 4.01 from AT&T * + SunOS2.[45] (SunOS2.3 has it!!) */ #include #define RUSAGE_SELF 0 #define RUSAGE_CHILDREN 1 struct rusage { struct timeval ru_utime;/* user time used */ struct timeval ru_stime;/* system time used */ int ru_maxrss; /* maximum resident set size */ int ru_ixrss; /* currently 0 */ int ru_idrss; /* integral resident set size */ int ru_isrss; /* currently 0 */ int ru_minflt; /* page faults not requiring physical I/O */ int ru_majflt; /* page faults requiring physical I/O */ int ru_nswap; /* swaps */ int ru_inblock; /* block input operations */ int ru_oublock; /* block output operations */ int ru_msgsnd; /* messages sent */ int ru_msgrcv; /* messages received */ int ru_nsignals; /* signals received */ int ru_nvcsw; /* voluntary context switches */ int ru_nivcsw; /* involuntary context switches */ }; int getrusage(int who, struct rusage *ru) { struct tms tm; if (times(&tm) < 0) return -1; ru->ru_utime.tv_sec= tm.tms_utime/CLK_TCK; ru->ru_utime.tv_usec= (tm.tms_utime%CLK_TCK)*(SKIPOVER/CLK_TCK); ru->ru_stime.tv_sec= tm.tms_stime/CLK_TCK; ru->ru_stime.tv_usec= (tm.tms_stime%CLK_TCK)*(SKIPOVER/CLK_TCK); return 0; } #endif /*RUSAGE_SELF*/ #define DEFAULT_FORMAT \ "%9b%8.2rt%8.2ct%12.4rbr%12.4cbr%8c%10.2rcr%10.1ccr" /* see at end of main what this means */ char UsageMessage[] = "\ Usage: nttcp [local options] host [remote options]\n\ local/remote options are:\n\ \t-t transmit data (default for local side)\n\ \t-r receive data\n\ \t-l# length of bufs written to network (default 4k)\n\ \t-m use IP/multicasting for transmit (enforces -t -u)\n\ \t-n# number of source bufs written to network (default 2048)\n\ \t-u use UDP instead of TCP\n\ \t-g#us gap in micro seconds between UDP packets (default 0s)\n\ \t-d set SO_DEBUG in sockopt\n\ \t-D don't buffer TCP writes (sets TCP_NODELAY socket option)\n\ \t-w# set the send buffer space to #kilobytes, which is\n\ \t dependent on the system - default is 16k\n\ \t-T print title line (default no)\n\ \t-f give own format of what and how to print\n\ \t-c compares each received buffer with expected value\n\ \t-s force stream pattern for UDP transmission\n\ \t-S give another initialisation for pattern generator\n\ \t-p# specify another service port\n\ \t-i behave as if started via inetd\n\ \t-R# calculate the getpid()/s rate from # getpid() calls\n\ \t-v more verbose output\n\ \t-V print version number and exit\n\ \t-? print this help\n\ \t-N remote number (internal use only)\n\ \tdefault format is: " DEFAULT_FORMAT "\n"; static char VersionString[] = "nttcp version " VERSION ", http://www.leo.org/~elmar/nttcp/\n"; /* global variables */ char MsgBuf[1024]; /* to generate formated messages there * be careful NOT to overwrite 1024 bytes!! */ char *myname; /* our name used in prints */ struct sockaddr_in dta_to; /* the destination where to send * the packets */ int SysCalls= 0; /* counts the number of read/write calls */ int ReportLimit=100; /* maximum number of failed comparisions to report */ int Remote= 0; /* ==1: we are the remote side */ char *version= VERSION; struct itimerval itval; #define SetItVal(msec) \ itval.it_interval.tv_sec= \ itval.it_interval.tv_usec= 0;\ itval.it_value.tv_sec=(msec)/1000; \ itval.it_value.tv_usec=((msec)%1000)*1000; \ setitimer(ITIMER_REAL, &itval, Nil(struct itimerval)) #define Suspend(msec) SetItVal(msec); pause() struct linger Ling = { 1, /* option on */ 1000000 /* linger time, for our control connection */ }; /* global variables needed by StartTimer, StopTimer */ TimeVal time0; /* Time at which timing started */ struct rusage ru0; /* rusage at start */ /* this describes the connection(s) to our remote site(s) */ typedef struct { char *HostName; char *IPName; int Socket; FILE *fin; FILE *fout; } RemoteConnection; RemoteConnection Peer[100]; int PeerCount=0; /* this structure is filled from the commandline options */ typedef struct { int PidCalls; /* do PidCalls to getpid() */ int udp; /* ==1: use UDP instead of TCP */ int Compare; /* compare the received data */ int StreamPattern; /* transmit a pattern stream */ int Transmit; /* ==1: transmitting, ==0 receiveing */ int Title; /* ==1: print title line */ int b_flag; /* ==1: use mread(), not used right now */ int SockOpt; /* Debug-SocketOptions */ int NoDelay; /* ==1: set TCP_NODELAY socket option */ int BufCnt; /* number of buffers to send */ int BufLen; /* buffer length */ int FixedDataSize; /* if>0: calc BufCnt/BufLen from this */ int Window, SndWin, RcvWin; /* TCP window sizes */ int Verbose; /* ==1: more diagnostics */ char *MulticastChannel; /* we send multicast traffic */ short MulticastPort; int GapLength; /* Gap length between packets */ int inetd; /* run in inetd mode */ short Service; /* the service-port inetd listens*/ char *Format; /* the output format */ char *RemHost; /* the remote host to connect to */ char *InitString; /* the stream init string */ int RemoteNumber; /* */ } Options; Options opt; /* the commandline options */ void fMessage(FILE *f, char *s) { if (Remote) { fprintf(f, "%s-%d: %s", myname, opt.RemoteNumber, s); } else { fprintf(f, "%s-l: %s", myname, s); } fflush(f); return; } #define Message(x) fMessage(stdout, x) void Exit(char *s, int ret) { syslog(LOG_DEBUG, s); fMessage(stderr,s); exit(ret); } void SigCld(int dummy) { wait(Nil(int)); /*fMessage(stderr, "Got SIGCLD\n");*/ signal(SIGCLD, SigCld); } void AlarmNothing(int dummy) { /*fMessage(stderr, "Got SIGALRM\n");*/ } char *AlarmMsg; void AlarmExit(int dummy) { fMessage(stderr, AlarmMsg); exit(9); } void SigPipe(int dummy) { Exit("got SIGPIPE, it seems our remote data socket dissapered.\n", 103); } void FetchRemoteMsg(char finCh) { int p, pcnt, fdcnt, fdmax; FdSet ReadMask, TestMask; char prevChar= '\0'; struct timeval tmo; FD_ZERO(&ReadMask); tmo.tv_sec= 10; tmo.tv_usec= 0; pcnt= fdmax=0; for (p=0; p fdmax) fdmax= Peer[p].Socket; } while (pcnt > 0) { if (opt.Verbose) { char MsgBuf[64]; sprintf(MsgBuf, "try to get outstanding messages from %d remote clients\n", pcnt); Message(MsgBuf); } FD_COPY(&ReadMask, &TestMask); fdcnt= SELECT(fdmax+1, &TestMask, 0, 0, &tmo); if (fdcnt <= 0) { break; } for (p=0; p 0) { MsgBuf[rc]= '\0'; fputs(MsgBuf, stdout); if ( /* test for end of this transmission */ (rc==1 && *MsgBuf == prevChar) || (rc>1 && MsgBuf[rc-1]==finCh&&MsgBuf[rc-2]==finCh)) { prevChar= MsgBuf[rc-1]; FD_CLR(Peer[p].Socket, &ReadMask); pcnt--; } } else if (rc <= 0) { FD_CLR(Peer[p].Socket, &ReadMask); close(Peer[p].Socket); Peer[p].Socket= -1; pcnt--; } } } } } void sysError(FILE *f, char *s) { syslog(LOG_DEBUG, "%s: %s, errno=%d\n", s, strerror(errno), errno); fMessage(f, s); fprintf(f, ": %s, errno=%d\n", strerror(errno), errno); fflush(f); } void exitError(char *s, int ret) { sysError(stderr, s); if (!Remote) /* local */ FetchRemoteMsg('\0'); fflush(stdout); exit(ret); } void tvAdd(TimeVal *tsum, TimeVal *t1, TimeVal *t0) { tsum->tv_sec = t0->tv_sec + t1->tv_sec; tsum->FRACT = t0->FRACT + t1->FRACT; if (tsum->FRACT > SKIPOVER) tsum->tv_sec++, tsum->FRACT -= SKIPOVER; } void tvSub(TimeVal *tdiff, TimeVal *t1, TimeVal *t0) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->FRACT = t1->FRACT - t0->FRACT; if (tdiff->FRACT < 0) tdiff->tv_sec--, tdiff->FRACT += SKIPOVER; } void StartTimer() { /* do anything to initialize time measurement. If you have no * getrusage or something like this, you may use anything that * seems appropriate to you. The only thing you have adhere to, * is the interface of StopTimer. */ gettimeofday(&time0, (struct timezone *)0); getrusage(RUSAGE_SELF, &ru0); NANO_ADJ(&time0); SysCalls=0; } void StopTimer(double *cput, double *realt) { /* delivers in cput: the amount of cpu time in seconds * in realt: the real time in seconds * both values are measured since the last call to StartTimer. * If only realtime can be measured, deliver the same value * to cput (you know what you are doing - right?) */ TimeVal timedol; struct rusage ru1; TimeVal td, tend, tstart; double secs; gettimeofday(&timedol, (struct timezone *)0); getrusage(RUSAGE_SELF, &ru1); NANO_ADJ(timedol); /* prusage(&ru0, &ru1, &timedol, &time0, line); (void)strncpy( str, line, len); */ /* Get real time */ tvSub(&td, &timedol, &time0); secs = td.tv_sec + FRACT_TO_SEC(td); if (secs < 0.00001) *realt= 0.00001; else *realt= secs; /* Get CPU time (user+sys) */ tvAdd(&tend, &ru1.ru_utime, &ru1.ru_stime); tvAdd(&tstart, &ru0.ru_utime, &ru0.ru_stime); tvSub(&td, &tend, &tstart); secs = td.tv_sec + FRACT_TO_SEC(td); if (secs < 0.00001) *cput= 0.00001; else *cput= secs; } double GetPidRate(int cnt) { int n= cnt; double cput; /* cpu time for cnt getpid() calls in s */ double realt; /* real time for cnt getpid() calls in s */ StartTimer(); while (--n > 0) { pid_t pid= getpid(); pid+= 1; /* to keep compilers quiet*/ } StopTimer(&cput, &realt); return ((double)cnt)/cput; } /*====================================================================*/ /* random definitions */ static unsigned char rb[250]; static int actp,actq; #define Advance250 \ if (++actp >= 250) \ actp=0; \ if (++actq >= 250) \ actq=0 #define Value250 \ rb[actp]^= rb[actq] unsigned char r250() { /* based on an article in Dr.Dobb's Journal May 1991 */ /* delivers a cyclic pseudo-random sequence of bytes */ /* the algorithm is based on the primitive polynom * x^250+x^130 mod 2 * The length of the sequence is 2^250-1 which is about * 10^73. This should be long enough. * We use here eight initial 250 bit sequences stored in * in the array rb, from where we generate the next bits. * The initialisatzion of these eight sequences must be * done with some care: they must be linear independent. * The key supplied to the program is used to initialize * the array rb. We set every element of rb four times. * Elmar Bartel */ Advance250; return Value250; } void streamr250(char *cp, int len) { while (len-- > 0) { Advance250; *(unsigned char *)cp++= Value250; } } void initr250(char *seed) { int q,i,w; char *p,b; memset(rb,0,sizeof(rb)); p=seed; w= 1; q= 0; /* Generate the seed in the array */ for (i=0; i<1000; i++) { w= (w* *p) % 1009; rb[q]= w & 0xff; if (++q>=250) q= 0; if (!*++p) p=seed; } /* make it linear independent */ b= -1; while (b) { rb[q]|= b; b<<=1; rb[q]^= b; if ((q+=37)>=250) q-= 250; } actp=0; actq=103; /* This is essential, it defines the primitive * polynom */ } /* end of random */ /*====================================================================*/ int SetTCP_NoDelay(int fd) { int one, ret; struct protoent *p; p = getprotobyname("tcp"); if (p == Nil(struct protoent)) return -1; ret= SetSockopt(fd, p->p_proto, TCP_NODELAY, (char *)&one, sizeof(one)); if (ret < 0) return ret; else return 0; } /* this is the original version * since isprint may be different depending on LOCALE * I replaced it. * ElB 1996-02-07 void Pattern(char *cp, int cnt) { int c = 0; while (cnt-- > 0) { while (!isprint((c&0x7F))) c++; *cp++ = (c++&0x7F); } } */ void Pattern(char *cp, int cnt) { streamr250(cp, cnt); } void StartPattern(char *InitString) { if (InitString) initr250(InitString); else initr250("This is a simple init string"); } /* * M R E A D * * This function performs the function of a read(II) but will * call read(II) multiple times in order to get the requested * number of characters. This can be necessary because * network connections don't deliver data with the same * grouping as it is written with. Written by Robert S. Miles, BRL. */ int mread(int fd, char *bufp, unsigned n) { unsigned count = 0; int nread; do { nread= read(fd, bufp, n-count); SysCalls++; if (nread < 0) { perror("ttcp_mread"); return(-1); } if (nread == 0) return (int)count; count+= (unsigned)nread; bufp+= nread; } while (count < n); return (int)count; } int Nread(int fd, char *buf, int count) { register int cnt; if (opt.udp) { struct sockaddr_in from; int len= sizeof(from); cnt= recvfrom(fd, buf, count, 0, (struct sockaddr *)&from, &len); SysCalls++; } else { /* if (opt.b_flag) cnt= mread(fd, buf, count); else { */ cnt= read(fd, buf, count); SysCalls++; /* } */ } return(cnt); } #define TV_DOUBLE(tv) ((tv)->tv_sec + (tv)->tv_usec*1e-6) void delay(int us) { /* This function tries to delay execution for us microseconds. * It is called to implement a defined frame-gap. But this is * a weak try, since unix is NOT a realtime operating system. * First, there is no portable way of doing real microsecond * sleeps - the resolution is about 10ms. Everything below exists * only if the manufacturer wants to supoort it. * Second nobody can assure that the packet we send off after * this delay is really sent. * For the first point, SunSolaris running on new Sparc hardware * (Sparc Station20, Ultra Sparc), can do a real usleep. * We try to do it in a most portable way, and try to make * it work, when the machine supports timings less than 10ms: * + everything greater than 10ms is done with select. * + the remaining time is wasted with looping through * gettimeofday. If the machine supports resolution lower * than 10ms this will do the delay on expense of CPU-time. * If not, it does no harm, but simply costs one call of * gettimeofday. */ struct timeval tv; long remain; double start, sec_delay; tv.tv_sec= us/(1000*1000); tv.tv_usec= us%(1000*1000); /* do it with select only to a resolution of clocktick */ remain= tv.tv_usec%(10*1000); tv.tv_usec= tv.tv_usec - remain; if (tv.tv_usec > 0 || tv.tv_sec > 0) (void)SELECT(1, 0, 0, 0, &tv); /* now try to make a better resolution */ sec_delay= remain*1e-6; gettimeofday(&tv, (struct timezone *)0); start= TV_DOUBLE(&tv); while (1) { struct timeval tv1; gettimeofday(&tv1, (struct timezone *)0); if (TV_DOUBLE(&tv1) - start >= sec_delay) break; } } int Nwrite(int fd, char *buf, int count) { int cnt; if (opt.StreamPattern) streamr250(buf, count); if (opt.udp) { again: cnt= sendto(fd, buf, count, 0, (struct sockaddr *)&dta_to, sizeof(dta_to)); SysCalls++; if (cnt < 0 && errno == ENOBUFS) { errno = 0; goto again; } } else { cnt= write(fd, buf, count); SysCalls++; } if (opt.GapLength) { delay(opt.GapLength); } return(cnt); } void Usage() { fputs(UsageMessage, stderr); fflush(stderr); exit(1); } void GetSizeValue(int *ac, char **av[], int *val, int Limit, char *what) { char *arg; int argc= *ac; char **argv= *av; if (argv[0][2] == '\0') { argc--, argv++; arg= argv[0]; } else arg= &argv[0][2]; *ac= argc; *av= argv; if (arg == NULL) { sprintf(MsgBuf, "missing argument value for %s\n", what); fMessage(stderr, MsgBuf); Usage(); } if ((*val=atoi(arg)) <= 0 || *val >= Limit) { sprintf(MsgBuf, "invalid value for %s (%.30s)\n", what, arg); fMessage(stderr, MsgBuf); Usage(); } } char* EightBits(char *bits, char value) { char bit=1; int i=sizeof(value)*8; bits[i]= '\0'; do { bits[--i]= (value&bit ? '1' : '0'); } while (bit<<=1); return bits; } int BufCompare(char *cp, char *expect, int leng, unsigned long nBytes, int BufLen, int *Reported) { int Failed=0; int n,ne; /*ne= nBytes%BufLen;*/ ne=0; for (n=0; n= BufLen) ne=0; */ ne++; } return Failed; } short GetService() { struct servent *ttcpService; ttcpService= getservbyname("ttcp", "tcp"); if (ttcpService == Nil(struct servent)) return TTCP_SERVICE; else return ntohs(ttcpService->s_port); } void InitOptions(Options *opt) { /*memset(opt, 0, sizeof(Options);*/ opt->PidCalls= 0; /* do PidCalls to getpid() */ opt->udp= 0; /* ==1: use UDP instead of TCP */ opt->Compare= 0; /* ==1: compare the received data */ opt->StreamPattern=0; /* transmit a pattern stream */ opt->Title=0; /* ==1: print title line */ opt->b_flag=0; /* ==1: use mread(), not used right now */ opt->SockOpt=0; /* Debug-SocketOptions */ opt->NoDelay=0; /* ==1: set TCP_NODELAY socket option */ opt->BufCnt=DEFAULT_BUFCNT; /* number of buffers to send */ opt->BufLen=DEFAULT_BUFLEN; /* buffer length */ opt->FixedDataSize=0; /* if>0: calc BufCnt/BufLen from this */ opt->Window=0; /* ==1: set the TCP window size */ opt->Verbose=0; /* ==1: more diagnostics */ opt->GapLength=0; /* no delay, send as fast as possible */ opt->inetd= 0; /* ==1: run in inetd mode */ opt->Service= GetService(); /* the service-port to listen to */ opt->MulticastChannel= NULL; opt->MulticastPort= 0; opt->Transmit=1; /* ==1: transmitting, ==0 receiveing */ opt->Format= DEFAULT_FORMAT; opt->InitString= Nil(char); return; } void ParseOptions(int *ac, char **av[], Options *opt) { int argc= *ac; char **argv= *av; argv++; argc--; while (argc>0 && argv[0][0] == '-') { switch (argv[0][1]) { case 't': opt->Transmit= 1; break; case 'r': opt->Transmit= 0; break; case 'T': opt->Title = 1; break; /* case 'B': opt->b_flag = 1; break; */ case 'd': opt->SockOpt|= SO_DEBUG; break; case 'D': opt->NoDelay = 1; break; case 'f': if (argv[0][2]) opt->Format= &argv[0][2]; else { argv++; argc--; opt->Format= argv[0]; } break; case 'g': GetSizeValue(&argc, &argv, &opt->GapLength, 10000000, "UDP gap-length"); break; case 'l': GetSizeValue(&argc, &argv, &opt->BufLen, 100000000, "bufferlength"); if (opt->FixedDataSize > 0 && opt->BufLen > 0) opt->BufCnt= opt->FixedDataSize / opt->BufLen; break; case 'n': GetSizeValue(&argc, &argv, &opt->BufCnt, 1000000000, "buffercount"); if (opt->FixedDataSize > 0 && opt->BufCnt > 0) opt->BufLen= opt->FixedDataSize / opt->BufCnt; break; case 'x': GetSizeValue(&argc, &argv, &opt->FixedDataSize, 1000000000, "fixed data size"); break; case 'N': GetSizeValue(&argc, &argv, &opt->RemoteNumber, 10, "RemoteNumber"); break; case 'w': GetSizeValue(&argc, &argv, &opt->SndWin, 10000, "windowsize"); opt->RcvWin = opt->SndWin= opt->SndWin*1024; opt->Window=1; break; case 'u': opt->udp = 1; break; case 'v': opt->Verbose = 1; break; case 'V': fputs(VersionString, stdout); exit(0); case '?': fputs(VersionString, stdout); fputs(UsageMessage, stdout); exit(0); case 's': opt->StreamPattern= 1; break; case 'c': opt->Compare= 1; break; case 'p': { int val; GetSizeValue(&argc, &argv, &val, 256*256, "service port"); opt->Service= (short)val; } break; case 'R': GetSizeValue(&argc, &argv, &opt->PidCalls, 2000000000, "pid calls"); break; case 'S': if (argv[0][2]) opt->InitString= &argv[0][2]; else { argv++; argc--; opt->InitString= argv[0]; } break; case 'i': opt->inetd= 1; break; case 'm': { #if defined(MULTICAST) char *p; if (argv[0][2]) opt->MulticastChannel= &argv[0][2]; else { argv++; argc--; opt->MulticastChannel= argv[0]; } if ( (p=strchr(opt->MulticastChannel, ':')) ) { *p= '\0'; opt->MulticastPort= atoi(p+1); } else opt->MulticastPort= DEFAULT_PORT; #else /*MULTICAST*/ strcpy(MsgBuf, "need -DMULTICAST when compile, to use MULTICAST"); fMessage(stderr, MsgBuf); Usage(); #endif /*MULTICAST*/ break; } default: { strcpy(MsgBuf, "unknown option: "); strncat(MsgBuf, argv[0], sizeof(MsgBuf)-strlen(MsgBuf)-1); fMessage(stderr, MsgBuf); Usage(); } } argv++; argc--; } /* opt->RemHost= argv[0]; */ *ac= argc; *av= argv; return; } int MakeArgvFromEnv(char *EnvName, char *argv[], int start, int Max) { /* fill argv at index start with the parts of argv * return the number of elements filled, or -1 on failure. */ char *env= getenv(EnvName); if (env != Nil(char)) { int ac= start; char *tok, *space= " \t\n"; char *nenv= strdup(env); if (nenv == Nil(char)) { return -1; } tok= strtok(nenv, space); while (tok != Nil(char)) { argv[ac++]= tok; if (ac == Max-1) break; /* silently stop parsing */ tok= strtok(Nil(char), space); } argv[ac]= NULL; return ac-start; } else { return 0; } } void ParseOptionsFromEnv(char *EnvName, Options *opt) { int ac, argc; /* parse options from the environment * only options will be looked at. Further * things will be simply ignored. * At most 50 options are parsed */ char **av_scratch, **argv= (char **)malloc(50*sizeof(char *)); if (argv == Nil(char *)) return; argc= 1; ac= MakeArgvFromEnv(EnvName, argv, argc, 50); if (ac < 0) { free(argv); return; } argc= ac+1; av_scratch= argv; ParseOptions(&argc, &av_scratch, opt); free(argv); return; } int createControlSocket(char *RemHost) { int rh; struct sockaddr_in sinrh; /* for control socket to remote host */ struct sockaddr_in sinlh; /* for control socket on local host */ memset(&sinrh, 0, sizeof(sinrh)); if (atoi(RemHost) > 0) { sinrh.sin_family = AF_INET; sinrh.sin_addr.s_addr = inet_addr(RemHost); Peer[PeerCount].IPName= RemHost; Peer[PeerCount].HostName= RemHost; } else { struct hostent *addr; if ((addr=gethostbyname(RemHost)) == NULL) { strcpy(MsgBuf, "bad hostname: "); strncat(MsgBuf, RemHost, sizeof(MsgBuf)-strlen(MsgBuf)-1); sysError(stderr, MsgBuf); return -6; } sinrh.sin_family = addr->h_addrtype; memcpy(&sinrh.sin_addr.s_addr, addr->h_addr, addr->h_length); Peer[PeerCount].IPName= strdup(inet_ntoa(sinrh.sin_addr)); Peer[PeerCount].HostName= RemHost; } sinrh.sin_port = htons(opt.Service); sinrh.sin_family = AF_INET; if ((rh = socket(AF_INET, SOCK_STREAM, 0)) < 0) { sysError(stderr, "control-socket"); return -7; } Peer[PeerCount].Socket= rh; memset(&sinlh, 0, sizeof(sinlh)); sinlh.sin_family = AF_INET; sinlh.sin_port = 0; /* our side has the free choice */ if (bind(rh, (struct sockaddr *)&sinlh, sizeof(sinlh)) < 0) { sysError(stderr, "bind"); close(rh); return -8; } if (connect(rh, (struct sockaddr *)&sinrh, sizeof(sinrh)) < 0) { sysError(stderr, "connect"); close(rh); return -9; } if (SetTCP_NoDelay(rh) < 0) { sysError(stderr, "cannot set TCP_NODELAY on protocol socket"); close(rh); return -10; } Peer[PeerCount].Socket= rh; PeerCount++; return 0; } void stripNewLine(char *s) { int l= strlen(s)-1; if (0 <= l && s[l] == '\n') s[l]= '\0'; return; } int main(int argc, char *argv[]) { struct sockaddr_in PeerAddr; int PeerAddrLeng; char *DataPortFormat= "dataport: %d\n"; int DataPort; struct sockaddr_in sinlh; /* for control socket on local host */ int fd; /* data socket to transport the data */ char *buf; /* the buffer to read from, to write to */ char *ExpectBuf= 0; /* when comparing the data */ unsigned long nBytes; /* the amount of transfered/received bytes */ unsigned long nBuffer; /* the number of buffers transferd/received */ double cput; /* cpu time for transmission in s */ double realt; /* real time for transmission in s */ if ((myname=strrchr(argv[0], '/')) != Nil(char)) myname++; else myname= argv[0]; if (getuid() == 0 || geteuid() == 0) { if (setuid(NOBODY) != 0) { sprintf(MsgBuf, "cannot setuid(%d)\n", NOBODY); Message(MsgBuf); exit(1); } } InitOptions(&opt); ParseOptionsFromEnv(NTTCP_LOC_OPT, &opt); ParseOptions(&argc, &argv, &opt); if (opt.Verbose) { sprintf(MsgBuf, "%s, version %s\n", myname, version); Message(MsgBuf); } if (opt.inetd) { /* we simulate inetd behaviour */ int nsrv, srv, fromleng; struct sockaddr_in sinsrv; struct sockaddr_in frominet; if (opt.Verbose) { sprintf(MsgBuf, "running in inetd mode on port %d - " "ignoring options beside -v and -p\n", opt.Service); Message(MsgBuf); } if ((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0) exitError("service-socket: socket", 99); memset(&sinsrv, 0, sizeof(sinsrv)); sinsrv.sin_family = AF_INET; sinsrv.sin_port = htons(opt.Service); if (bind(srv, (struct sockaddr *)&sinsrv, sizeof(sinsrv)) < 0) exitError("service-socket: bind:", 101); if (listen(srv, 2) < 0) exitError("service-socket", 103); signal(SIGCLD, SigCld); while (1) { int FailCnt; pid_t pid; fromleng = sizeof(frominet); memset(&frominet, 0, fromleng); while ((nsrv=accept(srv, (struct sockaddr *)&frominet, &fromleng)) < 0) if (errno == EINTR) continue; else break; if (nsrv < 0) exitError("service-accept", 115); FailCnt=0; while ((pid=fork()) < 0) { if (errno == EAGAIN && ++FailCnt < 100) { sprintf(MsgBuf, "fork failed for try #%d\n", FailCnt); Message(MsgBuf); signal(SIGALRM, AlarmNothing); Suspend(200); continue; } else break; } if (pid < 0) exitError("service-fork", 116); if (pid == 0) { /* child */ close(0); if (dup2(nsrv, 0) < 0) exitError("service-dup2(0)", 117); close(1); if (dup2(nsrv, 1) < 0) exitError("service-dup2(1)", 118); close(2); if (dup2(nsrv, 2) < 0) exitError("service-dup2(2)", 119); break; } if (opt.Verbose) { sprintf(MsgBuf, "forked child (pid=%d) on socket-fd=" "fd=%d after %d retries\n", (int)pid, nsrv, FailCnt); Message(MsgBuf); } close(nsrv); } } PeerAddrLeng= sizeof(PeerAddr); if (getpeername(0, (struct sockaddr *)&PeerAddr, &PeerAddrLeng) == 0) { /* assume we are the remote side, ie not started via commandline */ StrVec *OptionArg; struct hostent *PeerHost; char OptionLine[1024]; Remote= 1; Peer[PeerCount].IPName= strdup(inet_ntoa(PeerAddr.sin_addr)); PeerHost=gethostbyaddr((char *)&PeerAddr.sin_addr.s_addr, sizeof(PeerAddr.sin_addr.s_addr), PeerAddr.sin_family); openlog(myname, LOG_CONS, LOG_USER); if (PeerHost) Peer[PeerCount].HostName= strdup(PeerHost->h_name); else Peer[PeerCount].HostName= "?"; syslog(LOG_INFO, "call from %.50s (=%.30s)\n", Peer[PeerCount].HostName, Peer[PeerCount].IPName); if (SetSockopt(0, SOL_SOCKET, SO_LINGER, (char *)&Ling, sizeof(Ling))<0) exitError("setsockopt-linger", 2); if (SetSockopt(1, SOL_SOCKET, SO_LINGER, (char *)&Ling, sizeof(Ling))<0) exitError("setsockopt-linger", 2); /* don't buffer writes to our client */ if (SetTCP_NoDelay(1) < 0) exitError("cannot set TCP_NODELAY on protocol socket", 3); OptionLine[sizeof(OptionLine)-1]= '\0'; if (fgets(OptionLine, sizeof(OptionLine), stdin) == Nil(char)) { sprintf(MsgBuf, "%s: fgets: cannot read stdin\n", myname); Exit(MsgBuf, 2); } if (OptionLine[sizeof(OptionLine)-1] != '\0') { sprintf(MsgBuf, "%s: optionline longer than %d\n", myname, sizeof(OptionLine)-1); Exit(MsgBuf, 3); } OptionLine[strlen(OptionLine)-1]= '\0'; /* remove trainling newline */ OptionArg= StrVecFromString(OptionLine, '@'); argv= OptionArg->String; argc= OptionArg->Leng; myname= argv[0]; InitOptions(&opt); ParseOptions(&argc, &argv, &opt); if (opt.Verbose) { sprintf(MsgBuf, "Pid=%d, InetPeer= %.30s\n", (int)getpid(), Peer[PeerCount].IPName); Message(MsgBuf); sprintf(MsgBuf,"Optionline=\"%.201s\"\n", OptionLine); Message(MsgBuf); } Peer[PeerCount].Socket= 0; Peer[PeerCount].fin= stdin; Peer[PeerCount].fout= stdout; syslog(LOG_DEBUG, "call from %.50 (=%.30s): done remote initial processing\n", Peer[PeerCount].HostName, Peer[PeerCount].IPName); PeerCount++; } else { /*local*/ if (opt.Verbose) { sprintf(MsgBuf, "Pid=%d\n", (int)getpid()); Message(MsgBuf); } #if !defined(SunOS53) && !defined(SunOS54) if (errno != ENOTSOCK) { fprintf(stderr, "%s: getpeername %s\n", myname, strerror(errno)); exit(4); } #endif /*SunOS53,SunOS54*/ } if (opt.InitString || opt.StreamPattern) opt.Compare= 1; if (!opt.udp && opt.Compare) opt.StreamPattern= 1; StartPattern(opt.InitString); /* if there is are remaining options, we expect the first to be * the name of the host to connect to, and all further options * are passed to this host */ if (!Remote) { /* all the local processing needed to * set up data for the remote part */ int i, p; StrVec *RemOpt; char *RemOptStr, *RemNum; int RemOptLeng; char OptBuf[64]; if (argc <= 0) { fMessage(stderr, "don't know where to connect to\n"); Usage(); } i= 0; while (i 1) { #if defined(MULTICAST) /* assmume we want multicasting, even if not specified */ if (opt.MulticastChannel == NULL) { opt.MulticastChannel= DEFAULT_CHANNEL; opt.MulticastPort = DEFAULT_PORT; } opt.Transmit= 1; opt.udp= 1; #else /*MULTICAST*/ sprintf(MsgBuf, "cannot send to multiple hosts, " "use only the first one: %.50s (=%.30s)\n", Peer[0].HostName, Peer[0].IPName); Message(MsgBuf); PeerCount= 1; #endif /*MULTICAST*/ } RemOpt= StrVecCreate(10); StrVecAppend(RemOpt, myname); if (opt.Transmit) StrVecAppend(RemOpt, "-r"); else { StrVecAppend(RemOpt, "-t"); if (opt.NoDelay) StrVecAppend(RemOpt, "-D"); } { /* append option for remote side from environment */ char **av= (char **)malloc(50*sizeof(char *)); if (argv != Nil(char *)) { int ac= MakeArgvFromEnv(NTTCP_REM_OPT, av, 0, 50); StrVec *sv= StrVecFromArgv(ac, av); if (sv != Nil(StrVec)) { StrVecJoin(RemOpt, sv); StrVecDestroy(sv); } free(av); } } sprintf(OptBuf, "-l%d", opt.BufLen); StrVecAppend(RemOpt, OptBuf); sprintf(OptBuf, "-n%d", opt.BufCnt); StrVecAppend(RemOpt, OptBuf); if (opt.udp) StrVecAppend(RemOpt, "-u"); if (opt.Verbose) StrVecAppend(RemOpt, "-v"); if (opt.Compare) StrVecAppend(RemOpt, "-c"); if (opt.StreamPattern) StrVecAppend(RemOpt, "-s"); if (opt.Format) { StrVecAppend(RemOpt, "-f"); StrVecAppend(RemOpt, opt.Format); } if (opt.InitString) { StrVecAppend(RemOpt, "-S"); StrVecAppend(RemOpt, opt.InitString); } if (opt.PidCalls) { sprintf(OptBuf, "-R%d", opt.PidCalls); StrVecAppend(RemOpt, OptBuf); } if (opt.GapLength > 0) { sprintf(OptBuf, "-g%d", opt.GapLength); StrVecAppend(RemOpt, OptBuf); } if (opt.MulticastChannel) { sprintf(OptBuf, "-m%s:%d", opt.MulticastChannel, opt.MulticastPort); StrVecAppend(RemOpt, OptBuf); } StrVecAppend(RemOpt, "-Nx"); argv++; argc--; if (argc > 0) { StrVec *MoreOpt= StrVecFromArgv(argc, argv); StrVecJoin(RemOpt, MoreOpt); StrVecDestroy(MoreOpt); } RemOptStr= StrVecToString(RemOpt, '@'); RemOptLeng= strlen(RemOptStr); RemOptStr[RemOptLeng++]= '\n'; RemOptStr[RemOptLeng]= '\0'; RemNum= strstr(RemOptStr, "-Nx") + 2; for (p=0; p 0) break; } #if defined(MULTICAST) if (opt.MulticastChannel && DataPort != DEFAULT_PORT) { sprintf(MsgBuf, "receiving side %.50s (=%.30s) " "couldn't get multicast socket", Peer[p].HostName, Peer[p].IPName); Exit(MsgBuf, 14); } #endif /*MULTICAST*/ if (DataPort == 0) { sprintf(MsgBuf, "couldn't get dataport from receiving side " "\"%.50s\" (=%.30s)", Peer[p].HostName, Peer[p].IPName); Exit(MsgBuf, 15); } } /*syslog(LOG_DEBUG, "call from %.50s (=%.30s): setup dataport\n", Peer[0].HostName, Peer[0].IPName);*/ memset(&dta_to, 0, sizeof(dta_to)); dta_to.sin_family= AF_INET; if (opt.MulticastChannel) { dta_to.sin_addr.s_addr= inet_addr(opt.MulticastChannel); dta_to.sin_port= htons(opt.MulticastPort); } else { dta_to.sin_addr.s_addr= inet_addr(Peer[0].IPName); dta_to.sin_port= htons(DataPort); } if (opt.udp) { #if defined(MULTICAST) if (opt.MulticastChannel) { char loop; /* u_char ttl; * set onother ttl for multicast (default==1) * TTL 0 are restricted to the same host * TTL 1 are restricted to the same subnet * TTL 32 are restricted to the same site * TTL 64 are restricted to the same region * TTL 128 are restricted to the same continent * TTL 255 are unrestricted in scope. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { exitError("cannot set multicast TTL, 15); } */ /* we do not want to get our packets looped back */ loop= 0; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { exitError("cannot diable multicast loopback", 15); } } #endif /*MULTICAST*/ } else { /* == TCP */ memset(&sinlh, 0, sizeof(sinlh)); sinlh.sin_family = AF_INET; sinlh.sin_port = 0; /* our side has the free choice */ if (bind(fd, (struct sockaddr *)&sinlh, sizeof(sinlh)) < 0) exitError("bind-dta", 14); if (opt.Verbose & VERBOSE_CONNECT) { sprintf(MsgBuf, "waiting for connect\n"); Message(MsgBuf); } if (connect(fd, (struct sockaddr *)&dta_to, sizeof(dta_to)) < 0) { sprintf(MsgBuf, "connect-dta: fd=%d, sin_port=%d, s_addr=%s", fd, ntohs(dta_to.sin_port), inet_ntoa(dta_to.sin_addr)); exitError(MsgBuf, 15); } if (opt.Verbose & VERBOSE_CONNECT) { sprintf(MsgBuf, "connected !\n"); Message(MsgBuf); } if (opt.NoDelay) { if (SetTCP_NoDelay(fd) < 0) exitError("setsockopt: nodelay", 16); } } } else { /*Receive*/ memset(&sinlh, 0, sizeof(sinlh)); sinlh.sin_family = AF_INET; #if defined(MULTICAST) if (opt.MulticastChannel) { int ml, p, join_group; struct ip_mreq mreq; sinlh.sin_port = htons(opt.MulticastPort); if (bind(fd, (struct sockaddr *)&sinlh, sizeof(sinlh)) < 0) { char Message[256]; /*fprintf(Peer[0].fout, DataPortFormat, -1);*/ fprintf(Peer[0].fout, DataPortFormat, -1); sprintf(Message, "cannot bind to fixed multicast port %d\n", ntohs(sinlh.sin_port)); exitError(Message, 23); } /* join the multicast group */ mreq.imr_interface.s_addr= INADDR_ANY; mreq.imr_multiaddr.s_addr= inet_addr(opt.MulticastChannel); join_group= setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); if (join_group < 0) sprintf(MsgBuf, DataPortFormat, -1); else sprintf(MsgBuf, DataPortFormat, DEFAULT_PORT); /* tell it our clients */ ml= strlen(MsgBuf); for (p=0; p= 20000) { fprintf(Peer[0].fout, DataPortFormat, 0); exitError("too many bind calls on datasocket", 17); } else { listen(fd, 5); /* its essential to listen to this socket * BEFORE we tell the port our partner! * Otherwise the connect of our partner (if he * is fast enough) may be refused since we haven't * called listen yet. */ fprintf(Peer[0].fout, DataPortFormat, DataPort); } fflush(Peer[0].fout); } if (opt.udp) { /* nothing to do */ } else { /* == TCP */ struct sockaddr_in frominet; int fromleng; fromleng = sizeof(frominet); memset(&frominet, 0, fromleng); AlarmMsg= "accept timed out\n"; signal(SIGALRM, AlarmExit); SetItVal(80*1000); if ((fd=accept(fd, (struct sockaddr *)&frominet, &fromleng)) < 0) exitError("accept", 18); SetItVal(0); if (opt.Verbose) { struct sockaddr_in peer; int peerlen = sizeof(peer); if (getpeername(fd, (struct sockaddr *)&peer, &peerlen) < 0) exitError("getpeername", 19); sprintf(MsgBuf, "accept from %s\n", inet_ntoa(peer.sin_addr)); Message(MsgBuf); } } } /* if (SetSockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&Ling, sizeof(Ling))<0) exitError("fd setsockopt-linger", 22); */ /* now both sides have an open connection */ /* things to do on both sides */ if (opt.udp) { } else { /* !udp / TCP */ if (opt.SockOpt) { int one; if (SetSockopt(fd, SOL_SOCKET, opt.SockOpt, (char *)&one, sizeof(one)) < 0) exitError("setsockopt", 23); } if (opt.Window) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&opt.SndWin, sizeof(opt.SndWin)) < 0) exitError("setsockopt", 24); if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&opt.RcvWin, sizeof(opt.RcvWin)) < 0) exitError("setsockopt", 25); } } /* print window sizes */ if (opt.Verbose) { int optlen; int WinSize; optlen= sizeof(WinSize); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&WinSize, &optlen) < 0) strcpy(MsgBuf, "get send window size didn't work\n"); else sprintf(MsgBuf, "send window size = %d\n", WinSize); Message(MsgBuf); optlen= sizeof(WinSize); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&WinSize, &optlen) < 0) strcpy(MsgBuf, "get recv window size didn't work\n"); else sprintf(MsgBuf, "receive window size = %d\n", WinSize); Message(MsgBuf); } if (opt.Verbose) { sprintf(MsgBuf, "buflen=%d, bufcnt=%d, dataport=%d/%s%s\n", opt.BufLen, opt.BufCnt, DataPort, opt.udp?"udp":"tcp", Remote?"\n":""); Message(MsgBuf); if (!Remote) { /*Local*/ /* get messages from the other side(s) */ FetchRemoteMsg('\n'); } } if (opt.PidCalls) { /* do only the measure of the pidrate */ sprintf(MsgBuf, "pid-rate (from %d calls): %.0f/s\n", opt.PidCalls, GetPidRate(opt.PidCalls)); Message(MsgBuf); goto Fin; } if (opt.Transmit) { int n= opt.BufCnt; signal(SIGPIPE, SigPipe); if (opt.udp) { (void)Nwrite(fd, buf, 4); /* rcvr start */ nBytes= 0; nBuffer= 0; if (opt.StreamPattern) StartPattern(opt.InitString); else Pattern(buf, opt.BufLen); StartTimer(); while (n-- && Nwrite(fd, buf, opt.BufLen) == opt.BufLen) { nBytes+= opt.BufLen; nBuffer++; } StopTimer(&cput, &realt); opt.GapLength= 0; (void)Nwrite(fd, buf, 4); /* rcvr end */ sleep(2); (void)Nwrite(fd, buf, 4); /* rcvr end */ sleep(2); (void)Nwrite(fd, buf, 4); /* rcvr end */ } else { nBytes= 0; nBuffer= 0; StartTimer(); while (n-- && Nwrite(fd, buf, opt.BufLen) == opt.BufLen) { nBytes+= opt.BufLen; nBuffer++; } StopTimer(&cput, &realt); } if (opt.Verbose) { sprintf(MsgBuf, "transmitted %ld bytes\n", nBytes); Message(MsgBuf); } close(fd); syslog(LOG_DEBUG, "call from %.50s (=%.30s): everything transmitted\n", Peer[0].HostName, Peer[0].IPName); } else { /* receive */ int cnt; int ReportCnt; unsigned long FailedCnt=0; /* number of failed comparisions */ if (opt.Compare) { ReportCnt= FailedCnt= 0; if (!opt.StreamPattern) streamr250(ExpectBuf, opt.BufLen); } nBytes= 0; nBuffer= 0; if (opt.udp) { while ((cnt=Nread(fd, buf, opt.BufLen)) > 0) { static int going = 0; if (cnt <= 4) { if (going) { StopTimer(&cput, &realt); if (opt.Verbose) Message("got EOF\n"); break; /* "EOF" */ } else { going = 1; StartTimer(); } } else { nBuffer++; if (opt.StreamPattern) streamr250(ExpectBuf, cnt); if (opt.Compare) FailedCnt+= BufCompare(buf, ExpectBuf, cnt, nBytes, opt.BufLen, &ReportCnt); nBytes += cnt; } } } else { syslog(LOG_DEBUG, "call from %.50s (%.30s): start receiving\n", Peer[0].HostName, Peer[0].IPName); StartTimer(); while ((cnt=Nread(fd, buf, opt.BufLen)) > 0) { if (opt.Compare) { streamr250(ExpectBuf, cnt); FailedCnt+= BufCompare(buf, ExpectBuf, cnt, nBytes, opt.BufLen, &ReportCnt); } nBytes += cnt; nBuffer++; } StopTimer(&cput, &realt); syslog(LOG_DEBUG, "call from %.50s (%.30s: everything received\n", Peer[0].HostName, Peer[0].IPName); } if (opt.Verbose) { sprintf(MsgBuf, "received %ld bytes\n", nBytes); Message(MsgBuf); } if (opt.Compare && ReportCnt < FailedCnt) { sprintf(MsgBuf, "further %ld differences not reported\n", FailedCnt-ReportCnt); Message(MsgBuf); } #if defined(MULTICAST) if (opt.MulticastChannel) { /* leave the multicast group */ struct ip_mreq mreq; mreq.imr_interface.s_addr= INADDR_ANY; mreq.imr_multiaddr.s_addr= inet_addr(opt.MulticastChannel); setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); } #endif /*MULTICAST*/ } { /* now lets do our output. What is printed is determined * by a string with the following printf like format string. * The specifiers which are allowed: * l buffer-length in bytes (int) * n buffer-count (int) * b transfered bytes (int) * c calls (int) * rt real-time in s (float) * ct cpu-time in s (float) * rbr real bit rate in MBit/s (float) * cbr cpu bit rate in MBit/s (float) * rcr real call reate in Calls/s (float) * ccr cpu call rate in Calls/s (float) */ char *iFormat= "%*.*ld"; char *fFormat= "%*.*f"; char *fs; LenStr *TitleLine, *StatLine; fs= opt.Format; TitleLine= LenStrMake(" "); if (Remote) { sprintf(MsgBuf, "%d", opt.RemoteNumber); StatLine= LenStrMake(MsgBuf); } else StatLine= LenStrMake("l"); while (*fs) { char *cp, *nc; char *TitleStr; int fp, fw; cp= strchr(fs, '%'); if (cp) { int l= cp-fs; if (l >= sizeof(MsgBuf)) l= sizeof(MsgBuf)-1; LenStrncat(StatLine, fs, l); LenStrPadRight(TitleLine, ' ', LenStrLeng(TitleLine)+l); fs= cp; } else { LenStrcpy(StatLine, fs); LenStrPadRight(TitleLine, ' ', strlen(fs)); break; } fs++; nc= fs; fw= strtol(fs, &nc, 0); if (nc == fs) fw= 0; else fs= nc; if (*fs == '.') { fs++; nc= fs; fp=strtol(fs, &nc, 0); if (nc == fs) fp= 0; else fs=nc; } else fp= 0; if (fp>30) fp= 30; if (fw>30) fw= 30; /* * l buffer-length in bytes (int) * n buffer-count (int) * b transfered bytes (int) * c calls (int) * rt real-time in s (float) * rbr real bit rate in MBit/s (float) * rcr real call reate in Calls/s (float) * ct cpu-time in s (float) * cbr cpu bit rate in MBit/s (float) * ccr cpu call rate in Calls/s (float) */ if (strncmp(fs, "ccr", 3)==0) { sprintf(MsgBuf, fFormat, fw, fp, ((double)SysCalls)/cput); TitleStr= "CPU-C/s"; fs+= 3; } else if (strncmp(fs, "cbr", 3)==0) { sprintf(MsgBuf, fFormat, fw, fp, ((double)nBytes)/cput/125000); TitleStr= "CPU-MBit/s"; fs+= 3; } else if (strncmp(fs, "ct", 2)==0) { sprintf(MsgBuf, fFormat, fw, fp, cput); TitleStr= "CPU s"; fs+= 2; } else if (strncmp(fs, "rcr", 3)==0) { sprintf(MsgBuf, fFormat, fw, fp, ((double)SysCalls)/realt); TitleStr= "Real-C/s"; fs+= 3; } else if (strncmp(fs, "rbr", 3)==0) { sprintf(MsgBuf, fFormat, fw, fp, ((double)nBytes)/realt/125000); TitleStr= "Real-MBit/s"; fs+= 3; } else if (strncmp(fs, "rt", 2)==0) { sprintf(MsgBuf, fFormat, fw, fp, realt); TitleStr= "Real s"; fs+= 2; } else if (*fs == 'l') { sprintf(MsgBuf, iFormat, fw, fp, opt.BufLen); TitleStr= "BufLen"; fs++; } else if (*fs == 'n') { sprintf(MsgBuf, iFormat, fw, fp, nBuffer); TitleStr= "BufCnt"; fs++; } else if (*fs == 'b') { sprintf(MsgBuf, iFormat, fw, fp, nBytes); TitleStr= "Bytes"; fs++; } else if (*fs == 'c') { sprintf(MsgBuf, iFormat, fw, fp, SysCalls); TitleStr= "Calls"; fs++; } else continue; LenStrcat(StatLine, MsgBuf); sprintf(MsgBuf, "%*s", fw, TitleStr); LenStrcat(TitleLine, MsgBuf); } if (opt.Title) { LenStrApp(TitleLine, '\n'); fputs(LenStrString(TitleLine), stdout); } LenStrApp(StatLine, '\n'); fputs(LenStrString(StatLine), stdout); } Fin: if (Remote) { if (opt.Verbose) { Message("exiting\n\n"); } fflush(stdout); fflush(stderr); shutdown(0, 2); shutdown(1, 2); shutdown(2, 2); } else { FetchRemoteMsg('\n'); } exit(0); } nttcp-1.47/nttcp.1100644 310 310 25326 7146465375 12501 0ustar elmarelmar.\" This code was written and is copyrighted 1996,1997,1998 by .\" .\" Elmar Bartel .\" Institut fuer Informatik .\" Technische Universitaet Muenchen .\" bartel@informatik.tu-muenchen.de .\" .\" Permission to use, copy, modify and distribute this software .\" and its documentation for any purpose, except making money, is .\" herby granted, provided that the above copyright notice and .\" this permission appears in all places, where this code is .\" referenced or used literally. .\"/ .TH nttcp 1 "5 Oct 1998" .SH NAME nttcp \- new test TCP program .SH SYNOPSIS .B nttcp [ .B local options ] .B partner-host [ partner-host ] ... [ .B remote options ] .SH DESCRIPTION The .B nttcp program measures the transferrate (and other numbers) on a TCP, UDP or UDP multicast connection. To use .B nttcp you have to provide the executable on the local machine and on a partner machine. On the partner machine simply start .B nttcp with the option .BR "\-i" . Started this way, .B nttcp is waiting for connections from other .BR nttcp s. On the local host simply call .B nttcp with the name of the partner host. It will contact the .B nttcp started on the partner machine and initiate the transfer. On default the program transfers 2048 buffers of 4KByte length (a total of 8 MByte) to the partner host. On both sides the performance will be measured and the findings (both, remote and local) are reported on the local side. You may change nearly every parameter of the transmission via commandline options, even what and how results are printed. .SH OPTIONS .TP .B "\-r " defines the receive transfer direction; data is sent from the partner host to the local host. .TP .B "\-t " defines the transmit transfer direction; data is sent from the local host to the partner host. This is the default direction. .TP .B "\-T " Print a title line. .TP .B "\-u " Use the UDP protocol instead of TCP (which is the default). .TP .B "\-g " Gap time in microseconds between packets. This delay is implemented via the timeout parameter of select(2) and a loop with gettimeofday(2). The accuracy of this value is misleading. Most machines will not be able to delay exactly the given amount. The code will try its best to achieve the desired delay. For TCP connections this option does only implement a delay between the write(2) system calls. It does not really delay between the real output on the physical device. .TP .B "\-v " Give more and verbose output; only useful for debugging purposes. .TP .B "\-D " Set the TCP_NODELAY option on the transmitting socket. With this option set, the socket does not buffer any write requests. .TP .BI "\-f " "format string" Specify your own output format. See .BR OUTPUT . .TP .BI "\-n " "number of buffers" The given number of buffers will be written to the transmitting socket. It defaults to 2048. .TP .BI "\-l " "length of buffer" The given length defines the size of one buffer written to the transmitting socket. Defaults to 4096. .TP .BI "\-x " "fixed length of data" The given length defines the amount of data that will be transfered. Subsequent specified .BR "\-l " or .BR "\-n " options will adapt the corresponding other value so that the number of buffers and the length of buffer multiplies to the given fixed length. .TP .BI "\-w " "number of kilo bytes" Defines the buffer size of the transmitting and receiving socket. This is system dependant; usally it is 16K. .TP .B "\-c " If this option is present, the receiving side will compare the bytes received with the pattern used by the sending side. At most the first 100 differences will be reported. If the transmission is via TCP, a uniq pattern for the whole transmission is generated. For UDP the same pattern for each paket is used. You can force a stream pattern with the .B "\-s " switch; but if one paket is lost, all subsequent packets contain patterns not expected and will be reported as different. Since every byte is numbered, this can be used to detect the first packet lost during the transmission. .br .BR "BUT be aware" : if there is a difference, this option may lead to packet-losses on UDP transmissions or to degration in performance, since the preparation of the output is simple-minded and uses a lot of CPU time. .TP .B "\-s " Forces the generation of a stream pattern if UPD packet data is compared. See .B "\-c " switch. .TP .BI "\-S " "seed string" give any string to initialize the pattern generator. By default this seed has the value .RI ' "This is a simple init string" '. This enforces the .B "\-c" option. .TP .BI "\-p" "port number" On default the partner host will listen on port 5037. This can be overwritten with this option. .TP .B "\-i" If you have no root access on the partner host, or do not want hacking with .BR inetd , this option directs .B nttcp to behave as a daemon, waiting for connections and spawning off child processes by itself as inetd would do it otherwise. .TP .BI "\-R" "number of getpid() calls" This option does not transmit any data, but calls the given number of times getpid(2) and calculates the number of calls per second. This is a measure for the speed of the machine and the system call interface. .TP .BI "-m" "multicast IP:port" This option is used to force sending to the specified multicast address and port. This option enforces the .BR "\-u " and "\-t " switch. Also see MULTICAST later in this document. .SH OUTPUT The output of the program consists of two lines of numbers; or more lines if used in transmitting to more than one machine (multicasting). The first line for the measures of the local host the other line for the measure of the partner host. This is also indicated with the first characters beeing a 'l' respective 'r'. If the .B "\-T" flag was given, also a Title line is given. The default format of the outout looks like this: .br .sp .nf Bytes Real s CPU s Real-MBit/s CPU-MBit/s Calls Real-C/s CPU-C/s l 8388608 7.51 0.25 8.7307 259.8676 2048 272.83 8120.86 r 8388608 7.55 0.95 8.6804 68.9853 3831 507.42 4032.63 .fi .br .sp The timing and rate values marked with 'CPU' use the sum of system and user time only. Real timing and rate values are computed using the time from the begin to the end of the transmission. .br It is possible to specify another form of the output. This is done similiar to the format strings of printf(3s). The conversion characters of printf(3s) are replaced with the following tags. Each tag is preceded by '%' as in printf(3s). Between the '%' character and the tag there are width and precision specifications allowed as with printf(3s). Two types of values are printed integers and floats. For these types the conversion letters 'd' respective 'f' of printf(3s) are used. .br .TP 5 .BR "l " "prints the buffer length in bytes. Integer value." .TP 5 .BR "n " "prints the buffer count. Integer value." .TP 5 .BR "c " "prints the number of calls. Integer value." .TP 5 .BR "rt " "prints the real time in s. Float value." .TP 5 .BR "rbr " "prints the real bit rate in MBit/s. Float value." .TP 5 .BR "rcr " "prints the real call rate in calls/s. Float value." .TP 5 .BR "ct " "prints the cpu time in s. Float value." .TP 5 .BR "cbr " "prints the cpu bit rate in MBit/s. Float value." .TP 5 .BR "ccr " "prints the cpu call rate in calls/s. Float value." .PP The default format is produced with the following format string: .RS 4 "%9b%8.2rt%8.2ct%12.4rbr%12.4cbr%8c%10.2rcr%10.2ccr" .RE .SH INSTALLATION To make most convenient use of this program, it can be installed on the partner machine, so that inetd(8) can start it. To accomplish this, two files have to be edited: .B "/etc/inetd.conf" and .BR "/etc/services" "." .P The respective lines may look like this: .TP 3 .B "inetd.conf:" .nf ttcp stream tcp nowait nobody /usr/local/etc/nttcp nttcp .fi .TP 3 .B "services:" .nf ttcp 5037/tcp # to measure tcp transfer rates .fi .PP After these changes have been made, the inetd(8) process has to be notified via a HUP signal (or killed and restarted on older versions of unix). .PP .SH MULTICASTING Beginning with version 1.4 there is support generating multicast traffic. You even needn't set any option, but simply specify more than one partner host. This mode is restricted to sending packets from the local host to the partner hosts. And of course works only on machines that have a multicast enabled IP stack. Tested is this feature on Solaris2.6, HPUX-10 and HPUX-11 and Irix 6.2. Also FreeBSD-2.2.6 compiled with option MROUTING works. But be aware what this means to your networking environment. Most ethernet switches for example handle multicast traffic as broadcast. This way you will flood your complete network with these packets. .SH ENVIRONMENT The are two environment variables NTTCP_LOC_OPT and NTTCP_REM_OPT that can be used to preset the local options and remote options respectivly. They take the same format as the commandline does. Commandline options override those settings from the environment. .SH SECURITY Under security considerations, the inetd-mode of operation is .B NOT suggested. Hosts configured to start .B nttcp this way, are very open to denial-of-service attacks. If you are concerned about this issue, you should consider either the use of tcpwrapper or simply not install .B nttcp this way. .br Also be sure to run .B nttcp as non-root when started via inetd(8). I have taken some care to avoid buffer-overrun prone coding. But the source is too big now to be sure in all corners of the code. .PP You may also consider not to provide general access to this programm. It may easily be used to flood your network with lots of traffic. This may be used to launch or support denial-of-service attacks. .SH WARNING There are a lot of pitfalls in explaining unexpected measures. Be sure to get a thorough understanding of your network and the devices used and installed. Also it is extremly helpful to have a deep understanding of the things that happen in your machine and operating system. A short example shows what is meant here: If you see packet losses on UDP transfers, it may be, that the packets are lost on the sending host! For today machines it is easy to produce packets much faster than a 10MBit ethernet can swallow it, so they may be dropped on the UDP stack of the operating system. This depends on the implementation of your IP stack. So, to be sure, use a second machine, and snoop or tcpdump the traffic in question, to be sure what happens on the medium. .SH BUGS Any program without bugs? .SH SEE ALSO inetd(8). .SH HISTORY This program was written to ease the measurement of TCP transfer rates in a network of unix workstations. It is based on the ttcp.c program, which was (I suppose) posted to comp.sources.misc. This man-page describes version 1.4. .SH AUTHOR Elmar Bartel .br Fakultaet fuer Informatik, .br Technische Universitaet Muenchen. .PP bartel@informatik.tu-muenchen.de nttcp-1.47/support.c100644 310 310 14315 6162521503 13122 0ustar elmarelmar/* This code was written and is copyrighted 1994,1995,1996 by * * Elmar Bartel * Institut fuer Informatik * Technische Universitaet Muenchen * bartel@informatik.tu-muenchen.de * * Permission to use, copy, modify and distribute this software * and its documentation for any purpose, except making money, is * herby granted, provided that the above copyright notice and * this permission appears in all places, where this code is * referenced or used literally. */ /* This file is a partial copy from files of other packages. * Full information about these other packes can be obtained * from the author: * bartel@informatik.tu-muenchen.de */ #include #if defined(FreeBSD) #include #else #include #endif /*FreeBSD*/ #include "support.h" /* Pad the string to size from right with character pad. * CAUTION: No check is done to ensure there is room for the * additional characters. */ char *strrpad(char *s, char pad, int size) { int i= strlen(s); while (iLeng; } LenStr *LenStrApp(LenStr *ls, char ch) { if (ls->Leng == ls->MaxLeng) { ls->MaxLeng+= (ls->MaxLeng >> 3) + 1; ls->s= rNew(ls->s, ls->MaxLeng+1, char); } ls->s[ls->Leng++]= ch; ls->s[ls->Leng]= '\0'; return ls; } LenStr *LenStrEnsureSpace(LenStr *ls, int n) { if (ls->MaxLeng-ls->Leng < n) { char *ns= rNew(ls->s, n+1, char); if (ns == Nil(char)) return Nil(LenStr); else { ls->s= ns; return ls; } } else return ls; } LenStr *LenStrPadRight(LenStr *ls, char c, int n) { if (ls->Leng >= n) return ls; ls= LenStrEnsureSpace(ls, n); strrpad(ls->s, c, n); ls->Leng= n; return ls; } LenStr *LenStrcpy(LenStr *dvs, char *s) { int l= strlen(s); if (l >= dvs->MaxLeng) { dvs->s= rNew(dvs->s, l+1, char); dvs->MaxLeng= l; } strcpy(dvs->s, s); dvs->Leng= l; return dvs; } char *LenStrString(LenStr *ls) { return ls->s; } LenStr *LenStrncat(LenStr *dls, char *s, int n) { int l= strlen(s); if (l < n) n= l; if (n+dls->Leng >= dls->MaxLeng) { dls->MaxLeng= (dls->Leng + l); dls->s= rNew(dls->s, dls->MaxLeng+1, char); } strncat(dls->s+dls->Leng, s, n); dls->Leng+= n; dls->s[dls->Leng]= '\0'; return dls; } LenStr *LenStrMake(char *s) { LenStr *vs= NewLenStr; vs->MaxLeng= vs->Leng= strlen(s); vs->s= (char *)malloc(vs->Leng+1); strcpy(vs->s, s); return vs; } LenStr *LenStrcat(LenStr *dls, char *s) { int l= strlen(s); if (l+dls->Leng >= dls->MaxLeng) { dls->MaxLeng= (dls->Leng + l); dls->s= rNew(dls->s, dls->MaxLeng+1, char); } strcat(dls->s+dls->Leng, s); dls->Leng+= l; return dls; } LenStr *LenStrCreate(int l) { LenStr *vs= New(LenStr); vs->Leng= 0; vs->MaxLeng= l; vs->s= (char *)malloc(l+1); vs->s[0]= '\0'; return vs; } void LenStrDestroy(LenStr *ls) { if (ls->s) free(ls->s); free(ls); return; } StrVec *StrVecFromArgv(int argc, char *argv[]) { StrVec *sa= New(StrVec); int i; if (sa == Nil(StrVec)) return Nil(StrVec); if ((sa->String=nNew(argc, char *)) == Nil(char*)) { free(sa); return Nil(StrVec); } sa->Leng= 0; sa->MaxLeng= argc; for (i= 0; i sv->MaxLeng) { char **ss; if (sv->String==Nil(char *)) ss= nNew(n, char*); else ss= rNew(sv->String, n, char*); if (ss == Nil(char *)) return Nil(StrVec); sv->MaxLeng= n; sv->String= ss; } return sv; } StrVec *StrVecAppend(StrVec *sa, char *s) { char *ns; if (sa->Leng >= sa->MaxLeng) { StrVec *nsa; int nl= sa->MaxLeng; nl+= (nl>>3) + 4; if ((nsa=StrVecExpand(sa, nl)) == Nil(StrVec)) return Nil(StrVec); } if ((ns=strdup(s)) == Nil(char)) return Nil(StrVec); sa->String[sa->Leng++]= ns; return sa; } char *StrVecToString(StrVec *sa, char Sep) { int i, StrLeng= 0; char *str, *dstr; for (i=0; i< sa->Leng; i++) { char *cp= sa->String[i]; int l= 0; while (*cp) { l++; if (*cp == Sep) { l++; while (*cp && *cp==Sep) cp++; } else cp++; } StrLeng+= l+1; } dstr= str= nNew(StrLeng+1, char); if (dstr == Nil(char)) return Nil(char); for (i=0; i< sa->Leng; i++) { char *cp= sa->String[i]; if (i>0) *dstr++= Sep; while (*cp) { if (*cp == Sep) *dstr++= '\\'; *dstr++= *cp++; } } *dstr='\0'; return str; } StrVec *StrVecJoin(StrVec *sv, StrVec *sv1) { int i; sv= StrVecExpand(sv, sv->Leng+sv1->Leng); if (sv == Nil(StrVec)) return Nil(StrVec); for (i=0; iLeng; i++) { char *sd= strdup(sv1->String[i]); if (sd != Nil(char)) sv->String[sv->Leng++]= sd; else { while (--i>=0) free(sv->String[--sv->Leng]); return Nil(StrVec); } } return sv; } StrVec *StrVecCreate() { StrVec *sa= New(StrVec); if (sa == Nil(StrVec)) return Nil(StrVec); sa->Leng= 0; sa->MaxLeng= 0; sa->String= Nil(char*); return sa; } StrVec *StrVecFromString(char *s, char Sep) { char *scp, *rcp, *ss; StrVec *sa; if ((ss=strdup(s)) == Nil(char)) return Nil(StrVec); if ((sa=StrVecCreate()) == Nil(StrVec)) { free(ss); return Nil(StrVec); } rcp= ss; while (*rcp) { StrVec *nsa; char *start; while (*rcp == Sep) rcp++; scp= start= rcp; while (*rcp) { if (*rcp == '\\') { *scp++= *++rcp; if (*rcp) rcp++; continue; } if (*rcp == Sep) { if (*rcp) rcp++; *scp= '\0'; break; } *scp++= *rcp++; } if ((nsa=StrVecAppend(sa, start)) == Nil(StrVec)) { free(ss); StrVecDestroy(sa); return Nil(StrVec); } else sa= nsa; } free(ss); return sa; } void StrVecDestroy(StrVec *sa) { int i; for (i=0; iLeng; i++) free(sa->String[i]); if (sa->Leng > 0) free(sa->String); free(sa); return; } nttcp-1.47/support.h100644 310 310 6476 6111447221 13116 0ustar elmarelmar/* This code was written and is copyrighted 1996 by * * Elmar Bartel * Institut fuer Informatik * Technische Universitaet Muenchen * bartel@informatik.tu-muenchen.de * * Permission to use, copy, modify and distribute this software * and its documentation for any purpose, except making money, is * herby granted, provided that the above copyright notice and * this permission appears in all places, where this code is * referenced or used literally. */ /* This file is a partial copy of some other packages that contains * only those parts, that are used in nttcp * Full information about these pieces of software can be obtained * from the author: * bartel@informatik.tu-muenchen.de */ #ifndef NEW_INCLUDED extern void *LastMallocResult; #define Record(x) x #define New(type) (type *)(Record(malloc(sizeof(type)))) #define nNew(n,type) (type *)(Record(malloc((n)*sizeof(type)))) #define nZero(p,n,type) memset(p, 0, (n)*sizeof(type)) #define rNew(p,n,type) (type *)(Record(realloc(p, (n)*sizeof(type)))) #define Nil(type) (type *)0 #if !defined(ERROR_LOG2) #define ERROR_LOG2(s, a, b) fprintf(stderr, s, a, b) #endif /*ERROR_LOG2*/ #define Enlarge(p, t, Max, Failure) { \ t* NewP; \ int NewMax= (Max); \ NewMax+= ((NewMax)>>3) + 4; \ NewP= rNew(p, NewMax, t); \ if (NewP == Nil(t)) { \ ERROR_LOG2("%s: no memory for %d " \ #t "'s\n", myname, NewMax); \ Failure; \ } \ else { \ Max= NewMax; \ p= NewP; \ } \ } #endif /*NEW_INCLUDED*/ /* This code was written and is copyrighted by * * Elmar Bartel * Institut fuer Informatik * Technische Universitaet Muenchen * bartel@informatik.tu-muenchen.de * * Permission to use, copy, modify and distribute this software * and its documentation for any purpose except making money is * herby granted, provided that the above copyright notice and * this permission appear in all copies and supporting * documentation. * */ #ifndef LSTR_INCLUDED #define LSTR_INCLUDED #ifndef Nil #define Nil(type) (type *)0 #define New(type) (type *)malloc(sizeof(type)) #define nNew(n, type) (type *)malloc((n)*sizeof(type)) #define rNew(p, n, type) (type *)realloc(p,(n)*sizeof(type)) #endif /*Nil*/ typedef struct { int Leng; int MaxLeng; char *s; } LenStr; #define NilLenStr (LenStr *)0 #define NewLenStr (LenStr *)malloc(sizeof(LenStr)) void LenStrDestroy (LenStr *ls); LenStr *LenStrCreate (int l); LenStr *LenStrcat (LenStr *dls, char *s); LenStr *LenStrMake (char *s); LenStr *LenStrncat (LenStr *dls, char *s, int n); char *LenStrString (LenStr *ls); LenStr *LenStrcpy (LenStr *dvs, char *s); LenStr *LenStrPadRight (LenStr *ls, char pad, int n); LenStr *LenStrApp (LenStr *ls, char ch); int LenStrLeng (LenStr *ls); #endif /*LSTR_INCLUDED*/ #ifndef STRVEC_INCLUDED #define STRVEC_INCLUDED typedef struct { int Leng; int MaxLeng; char **String; } StrVec; StrVec *StrVecCreate (); void StrVecDestroy (StrVec *sa); StrVec *StrVecFromString (char *s, char Sep); StrVec *StrVecJoin (StrVec *sv, StrVec *sv1); char *StrVecToString (StrVec *sv, char Sep); StrVec *StrVecAppend (StrVec *sa, char *s); StrVec *StrVecFromArgv (int argc, char *argv[]); #endif /*STRVEC_INCLUDED*/ #if defined(ultrix) #define strdup(x) strcpy((char *)malloc(strlen(x)+1), x) #endif /*ultrix*/ nttcp-1.47/README100644 310 310 3752 6775150071 12115 0ustar elmarelmarThis is README of nttcp-1.4: This program is a much more convenient version of the ttcp program. It uses inetd (or simulates its behaviour) to start off the remote side program which will send/receive data. Both sides measure the time and number of bytes transfered. The local side will print the measures. The format of the output can be specified on the commandline. ==> More info can be found in the manpage nttcp.1 -- especially in the section INSTALLATION. For a first try you may simply call: make. If this does not work, look for an option setting suitable for your operating system / compiler. Set this from the commandline or from the Makefile (I prefer to set ARCH, OPT, DBG from the commandline). If nothing works, let me know. You may tell me also if it works! Elmar Bartel, 1997-06-26. Elmar.Bartel@in.tum.de ________________________________________________________________________ What's new since 1.3: . multicast support: Just put more destination hosts on the commandline. Multicast is only available on systems, that support multicast in their IP implementation: Solaris 5.*, IRIX [56].*, HPUX10/HPUX11 OSF/1 FreeBSD if compiled with MROUTING . Two environment variables NTTCP_LOC_OPT and NTTCP_REM_OPT to override builtin default values. . remarks to SECURITY in man page . reset uid if we are executing as root Elmar Bartel, 1998-10-06. ________________________________________________________________________ What's new since 1.2: -g switch to configure the gap time between UDP packets . some clarifying comments in the source and man page Elmar Bartel, 1997-06-26. bartel@informatik.tu-muenchen.de bartel@informatik.tu-muenchen.de ________________________________________________________________________ What's new since 1.1: -S switch to initialize the pattern generator -p switch to use another port to listen to -i switch simulate inetd behaviour. -R switch to caclulate the getpid()-calls per second nttcp-1.47/Makefile100644 310 310 5675 7217362060 12676 0ustar elmarelmar# This code was written and is copyrighted 1996,1998 by: # # Elmar Bartel # Institut fuer Informatik # Technische Universitaet Muenchen # bartel@informatik.tu-muenchen.de # # Permission to use, copy, modify and distribute this software # and its documentation for any purpose, except making money, is # herby granted, provided that the above copyright notice and # this permission appears in all places, where this code is # referenced or used literally. # uncomment the settings for your system and type make. # if there is no setting available, simply try to use # the most closest settings. # Finally let me know what you used, to get it running. ## For System V4.1 (AT&T) either cc or gcc ## And for SINIX V4.1 (either mips or intel). #ARCH= #LIB= -lsocket -lnsl #CC= cc #OPT= -O #DBG= -g #INC= ## For DEC (Mips)ULTRIX 4.1 #ARCH= -Dultrix #LIB= #CC= gcc #OPT= -O2 #DBG= #INC= ## For HPUX 9.* with gcc #ARCH= -Dhpux9 #LIB= #CC= gcc #OPT= -O2 #DBG= -g #INC= ## For HPUX 9.* with cc #ARCH= -Ae -Dhpux9 #LIB= #CC= cc #OPT= -O #DBG= -g #INC= ## For HPUX 10.* with gcc #ARCH= -Dhpux #LIB= #CC= gcc #OPT= -O2 #DBG= -g #INC= ## For HPUX 10.* with cc #ARCH= -Ae -Dhpux #LIB= #CC= cc #OPT= -O #DBG= -g #INC= ## For SunOS 4.1 with clcc #ARCH=-DSunOS4 #LIB= #OPT= -O2 #CC= clcc #DBG= #INC= ## For SunOS 4.1 with gcc #ARCH=-DSunOS4 #LIB= #OPT= -O2 #CC= gcc #DBG= #INC= ## For SunOS 5.3 with gcc #ARCH=-DSunOS53 #LIB= -lsocket -lnsl -lucb #OPT= -O2 #CC= gcc #DBG= #INC= ## For SunOS 5.[456] with cc from SunSoft #ARCH=-DSunOS54 #LIB= -lsocket -lnsl #OPT= -O #CC= /usr/ccs/bin/cc #CC= /opt/SUNWspro/bin/cc #DBG= -g #INC= ## For SunOS 5.[456] with gcc #ARCH=-DSunOS54 #LIB= -lsocket -lnsl #OPT= -O2 #CC= gcc #DBG= -Wall -g #INC= # For FreeBSD ARCH= -DFreeBSD LIB= OPT= -O2 CC= cc DBG= INC= ## For SGI IRIX 5.3, 6.2 (cc or gcc) #ARCH= #LIB= #OPT= -O #CC= cc #DBG= #INC= ## For IBM AIX 2.3 #ARCH= -Daix #LIB= #OPT= -O #CC= cc #DBG= #INC= ## For OSF/1 10.41 #ARCH= #LIB= #OPT= -O -Olimit 700 #CC= cc #DBG= #INC= ## For Linux with gcc #ARCH= #LIB= #OPT= -O2 #CC= gcc #DBG= #INC= VERSION= 1.47 #prefix= /usr/local/dist/DIR/nttcp-$(VERSION) #prefix= /usr/local prefix= /usr/local/opt/nttcp-$(VERSION) CFLAGS = $(ARCH) $(DBG) $(DEF) $(INC) $(OPT) -DVERSION=\"$(VERSION)\" LFLAGS = $(DBG) nttcp: nttcp.o support.o $(CC) $(LFLAGS) nttcp.o -o nttcp support.o $(LIB) support.o: support.c support.h dist: if [ -d nttcp-$(VERSION) ]; then \ rm -f nttcp-$(VERSION)/*; \ else \ mkdir nttcp-$(VERSION); \ fi ln nttcp.c nttcp.1 support.c support.h \ README Makefile nttcp-$(VERSION) tar cvf - nttcp-$(VERSION) | gzip > /tmp/nttcp-$(VERSION).tar.gz; \ zip -lgr /tmp/nttcp`echo $(VERSION)|tr -d .`.zip nttcp-$(VERSION) install: nttcp -mkdir -p $(prefix)/bin -mkdir -p $(prefix)/man/man1 cp nttcp $(prefix)/bin cp nttcp.1 $(prefix)/man/man1 clean: rm -f *.o clobber: clean rm -rf core nttcp nttcp-$(VERSION)