/****************************************************************************** * File: pit_server.c * * Description: Contains source code for an IPv6-capable 'PIT' server. * This is a derivative of the tod6 (time-of-day) server that was written * by John Wenker. * ....... * Author of tod6: John Wenker, Sr. Software Engineer, * Performance Technologies, San Diego, USA * ....... * The program tod6 was a time of day server. It has beeen modified * to provide a microsecond timestamp on request. Modified and adapted * for PIT purposes by Don Capps. [ capps@iozone.org ] * * This server sends the current value of gettimeofday() in * microseconds back to the client, as a numerical string. * * /etc/services should contain "PIT" with a specified port value. * ******************************************************************************/ /* ** System header files. */ #include /* errno declaration & error codes. */ #include /* getaddrinfo(3) et al. */ #include /* sockaddr_in & sockaddr_in6 definition. */ #include /* printf(3) et al. */ #include /* exit(2). */ #include /* String manipulation & memory functions. */ #if defined(_SUA_) #include /* poll(2) and related definitions. */ #else #include /* poll(2) and related definitions. */ #endif #include /* Socket functions (socket(2), bind(2), etc). */ #include /* time(2) & ctime(3). */ #include /* gettimeofday */ #include /* getopt(3), read(2), etc. */ /* Include for Cygnus development environment for Windows */ #if defined (Windows) #include int errno; #endif #if defined(_SUA_) extern char *optarg, *opterr; #endif /* ** Constants. ** ** Please remember to add PIT service to the /etc/services file. */ #define DFLT_SERVICE "PIT" /* Programmable Interdimensional Timer */ #define INVALID_DESC -1 /* Invalid file descriptor. */ #define MAXCONNQLEN 3 /* Max nbr of connection requests to queue. */ #define MAXTCPSCKTS 2 /* One TCP socket for IPv4 & one for IPv6. */ #define MAXUDPSCKTS 2 /* One UDP socket for IPv4 & one for IPv6. */ #define VALIDOPTS "vh:p:" /* Valid command options. */ /* ** Simple boolean type definition. */ int false = 0; int true = 1; /* ** Prototypes for internal helper functions. */ static int openSckt( const char *service, const char *protocol, int desc[ ], size_t *descSize ); static void pit( int tSckt[ ], size_t tScktSize, int uSckt[ ], size_t uScktSize ); /* ** Global data objects. */ static char hostBfr[ NI_MAXHOST ]; /* For use w/getnameinfo(3). */ static const char *pgmName; /* Program name w/o dir prefix. */ static char servBfr[ NI_MAXSERV ]; /* For use w/getnameinfo(3). */ static int verbose = 0; /* Verbose mode indication. */ struct timeval tm; /* Timeval structure, used with gettimeofday() */ char timeStr[40]; /* String for time in microseconds */ char service_name[20]; int need; /* ** Usage macro for command syntax violations. */ #define USAGE \ { \ fprintf( stderr, \ "Usage: %s [-v] -p service \n", \ pgmName ); \ exit( 127 ); \ } /* End USAGE macro. */ /* ** Macro to terminate the program if a system call error occurs. The system ** call must be one of the usual type that returns -1 on error. */ #define CHK(expr) \ do \ { \ if ( (expr) == -1 ) \ { \ fprintf( stderr, \ "%s (line %d): System call ERROR - %s.\n", \ pgmName, \ __LINE__, \ strerror( errno ) ); \ exit( 1 ); \ } /* End IF system call failed. */ \ } while ( false ) /****************************************************************************** * Function: main * * Description: * Set up a PIT server and handle network requests. This server * handles both TCP and UDP requests. * * Parameters: * The usual argc and argv parameters to a main() function. * * Return Value: * This is a daemon program and never returns. However, in the degenerate * case where no sockets are created, the function returns zero. ******************************************************************************/ int main( int argc, char *argv[ ] ) { int opt; int tSckt[ MAXTCPSCKTS ]; /* Array of TCP socket descriptors. */ size_t tScktSize = MAXTCPSCKTS; /* Size of uSckt (# of elements). */ int uSckt[ MAXUDPSCKTS ]; /* Array of UDP socket descriptors. */ size_t uScktSize = MAXUDPSCKTS; /* Size of uSckt (# of elements). */ strcpy(service_name,DFLT_SERVICE); /* ** Set the program name (w/o directory prefix). */ pgmName = strrchr( argv[ 0 ], '/' ); pgmName = pgmName == NULL ? argv[ 0 ] : pgmName + 1; /* ** Process command options. */ opterr = 0; /* Turns off "invalid option" error messages. */ while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) >= 0 ) { switch ( opt ) { case 'v': /* Verbose mode. */ { verbose = true; break; } case 'p': /* Get the port number */ { strcpy(service_name,optarg); need++; break; } default: { USAGE; } } /* End SWITCH on command option. */ } /* End WHILE processing options. */ if(need < 1) { USAGE; exit; } /* ** Open both a TCP and UDP socket, for both IPv4 & IPv6, on which to receive ** service requests. */ if ( ( openSckt( service_name, "tcp", tSckt, &tScktSize ) < 0 ) || ( openSckt( service_name, "udp", uSckt, &uScktSize ) < 0 ) ) { exit( 1 ); } /* ** Run the Programmable Interdimensional Timer server. */ if ( ( tScktSize > 0 ) || ( uScktSize > 0 ) ) { pit( tSckt, /* pit() never returns. */ tScktSize, uSckt, uScktSize ); } /* ** Since pit() never returns, execution only gets here if no sockets were ** created. */ if ( verbose ) { fprintf( stderr, "%s: No sockets opened... terminating.\n", pgmName ); } return 0; } /* End main() */ /****************************************************************************** * Function: openSckt * * Description: * Open passive (server) sockets for the indicated inet service & protocol. * Notice in the last sentence that "sockets" is plural. During the interim * transition period while everyone is switching over to IPv6, the server * application has to open two sockets on which to listen for connections... * one for IPv4 traffic and one for IPv6 traffic. * * Parameters: * service - Pointer to a character string representing the well-known port * on which to listen (can be a service name or a decimal number). * protocol - Pointer to a character string representing the transport layer * protocol (only "tcp" or "udp" are valid). * desc - Pointer to an array into which the socket descriptors are * placed when opened. * descSize - This is a value-result parameter. On input, it contains the * max number of descriptors that can be put into 'desc' (i.e. the * number of elements in the array). Upon return, it will contain * the number of descriptors actually opened. Any unused slots in * 'desc' are set to INVALID_DESC. * * Return Value: * 0 on success, -1 on error. ******************************************************************************/ static int openSckt( const char *service, const char *protocol, int desc[ ], size_t *descSize ) { struct addrinfo *ai; int aiErr; struct addrinfo *aiHead; struct addrinfo hints = { .ai_flags = AI_PASSIVE, /* Server mode. */ .ai_family = PF_UNSPEC }; /* IPv4 or IPv6. */ size_t maxDescs = *descSize; /* ** Initialize output parameters. When the loop completes, *descSize is 0. */ while ( *descSize > 0 ) { desc[ --( *descSize ) ] = INVALID_DESC; } /* ** Check which protocol is selected (only TCP and UDP are valid). */ if ( strcmp( protocol, "tcp" ) == 0 ) /* TCP protocol. */ { hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; } else if ( strcmp( protocol, "udp" ) == 0 ) /* UDP protocol. */ { hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; } else /* Invalid protocol. */ { fprintf( stderr, "%s (line %d): ERROR - Unknown transport " "layer protocol \"%s\".\n", pgmName, __LINE__, protocol ); return -1; } /* ** Look up the service's "well-known" port number. Notice that NULL is being ** passed for the 'node' parameter, and that the AI_PASSIVE flag is set in ** 'hints'. Thus, the program is requesting passive address information. ** The network address is initialized to :: (all zeros) for IPv6 records, or ** 0.0.0.0 for IPv4 records. */ if ( ( aiErr = getaddrinfo( NULL, service, &hints, &aiHead ) ) != 0 ) { fprintf( stderr, "%s (line %d): ERROR - %s.\n", pgmName, __LINE__, gai_strerror( aiErr ) ); return -1; } /* ** For each of the address records returned, attempt to set up a passive ** socket. */ for ( ai = aiHead; ( ai != NULL ) && ( *descSize < maxDescs ); ai = ai->ai_next ) { if ( verbose ) { /* ** Display the current address info. Start with the protocol- ** independent fields first. */ fprintf( stderr, "Setting up a passive socket based on the " "following address info:\n" " ai_flags = 0x%02X\n" " ai_family = %d (PF_INET = %d, PF_INET6 = %d)\n" " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d)\n" " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d)\n" " ai_addrlen = %d (sockaddr_in = %lu, " "sockaddr_in6 = %lu)\n", ai->ai_flags, ai->ai_family, PF_INET, PF_INET6, ai->ai_socktype, SOCK_STREAM, SOCK_DGRAM, ai->ai_protocol, IPPROTO_TCP, IPPROTO_UDP, ai->ai_addrlen, sizeof( struct sockaddr_in ), sizeof( struct sockaddr_in6 ) ); /* ** Now display the protocol-specific formatted socket address. Note ** that the program is requesting that getnameinfo(3) convert the ** host & service into numeric strings. */ getnameinfo( ai->ai_addr, ai->ai_addrlen, hostBfr, sizeof( hostBfr ), servBfr, sizeof( servBfr ), NI_NUMERICHOST | NI_NUMERICSERV ); switch ( ai->ai_family ) { case PF_INET: /* IPv4 address record. */ { struct sockaddr_in *p = (struct sockaddr_in*) ai->ai_addr; fprintf( stderr, " ai_addr = sin_family: %d (AF_INET = %d, " "AF_INET6 = %d)\n" " sin_addr: %s\n" " sin_port: %s\n", p->sin_family, AF_INET, AF_INET6, hostBfr, servBfr ); break; } /* End CASE of IPv4. */ case PF_INET6: /* IPv6 address record. */ { struct sockaddr_in6 *p = (struct sockaddr_in6*) ai->ai_addr; fprintf( stderr, " ai_addr = sin6_family: %d (AF_INET = %d, " "AF_INET6 = %d)\n" " sin6_addr: %s\n" " sin6_port: %s\n" " sin6_flowinfo: %d\n" " sin6_scope_id: %d\n", p->sin6_family, AF_INET, AF_INET6, hostBfr, servBfr, p->sin6_flowinfo, p->sin6_scope_id ); break; } /* End CASE of IPv6. */ default: /* Can never get here, but just for completeness. */ { fprintf( stderr, "%s (line %d): ERROR - Unknown protocol family (%d).\n", pgmName, __LINE__, ai->ai_family ); freeaddrinfo( aiHead ); return -1; } /* End DEFAULT case (unknown protocol family). */ } /* End SWITCH on protocol family. */ } /* End IF verbose mode. */ /* ** Create a socket using the info in the addrinfo structure. */ CHK( desc[ *descSize ] = socket( ai->ai_family, ai->ai_socktype, ai->ai_protocol ) ); /* ** Here is the code that prevents "IPv4 mapped addresses", as discussed ** in Section 22.1.3.1. If an IPv6 socket was just created, then set the ** IPV6_V6ONLY socket option. */ if ( ai->ai_family == PF_INET6 ) { #if defined( IPV6_V6ONLY ) /* ** Disable IPv4 mapped addresses. */ int v6Only = 1; CHK( setsockopt( desc[ *descSize ], IPPROTO_IPV6, IPV6_V6ONLY, &v6Only, sizeof( v6Only ) ) ); #else /* ** IPV6_V6ONLY is not defined, so the socket option can't be set and ** thus IPv4 mapped addresses can't be disabled. Print a warning ** message and close the socket. Design note: If the ** #if...#else...#endif construct were removed, then this program ** would not compile (because IPV6_V6ONLY isn't defined). That's an ** acceptable approach; IPv4 mapped addresses are certainly disabled ** if the program can't build! However, since this program is also ** designed to work for IPv4 sockets as well as IPv6, I decided to ** allow the program to compile when IPV6_V6ONLY is not defined, and ** turn it into a run-time warning rather than a compile-time error. ** IPv4 mapped addresses are still disabled because _all_ IPv6 traffic ** is disabled (all IPv6 sockets are closed here), but at least this ** way the server can still service IPv4 network traffic. */ fprintf( stderr, "%s (line %d): WARNING - Cannot set IPV6_V6ONLY socket " "option. Closing IPv6 %s socket.\n", pgmName, __LINE__, ai->ai_protocol == IPPROTO_TCP ? "TCP" : "UDP" ); CHK( close( desc[ *descSize ] ) ); continue; /* Go to top of FOR loop w/o updating *descSize! */ #endif /* IPV6_V6ONLY */ } /* End IF this is an IPv6 socket. */ /* ** Bind the socket. Again, the info from the addrinfo structure is used. */ CHK( bind( desc[ *descSize ], ai->ai_addr, ai->ai_addrlen ) ); /* ** If this is a TCP socket, put the socket into passive listening mode ** (listen is only valid on connection-oriented sockets). */ if ( ai->ai_socktype == SOCK_STREAM ) { CHK( listen( desc[ *descSize ], MAXCONNQLEN ) ); } /* ** Socket set up okay. Bump index to next descriptor array element. */ *descSize += 1; } /* End FOR each address info structure returned. */ /* ** Dummy check for unused address records. */ if ( verbose && ( ai != NULL ) ) { fprintf( stderr, "%s (line %d): WARNING - Some address records were " "not processed due to insufficient array space.\n", pgmName, __LINE__ ); } /* End IF verbose and some address records remain unprocessed. */ /* ** Clean up. */ freeaddrinfo( aiHead ); return 0; } /* End openSckt() */ /****************************************************************************** * Function: pit * * Description: * Listen on a set of sockets and send the current microsecond counter * that was produced by gettimeofday(), to any clients. This function * never returns. * * Parameters: * tSckt - Array of TCP socket descriptors on which to listen. * tScktSize - Size of the tSckt array (nbr of elements). * uSckt - Array of UDP socket descriptors on which to listen. * uScktSize - Size of the uSckt array (nbr of elements). * * Return Value: None. ******************************************************************************/ static void pit( int tSckt[ ], size_t tScktSize, int uSckt[ ], size_t uScktSize ) { char bfr[ 256 ]; ssize_t count; struct pollfd *desc; size_t descSize = tScktSize + uScktSize; int idx; int newSckt; struct sockaddr *sadr; socklen_t sadrLen; struct sockaddr_storage sockStor; int status; size_t timeLen; time_t timeVal; ssize_t wBytes; unsigned long long secs; int ret; /* ** Allocate memory for the poll(2) array. */ desc = malloc( descSize * sizeof( struct pollfd ) ); if ( desc == NULL ) { fprintf( stderr, "%s (line %d): ERROR - %s.\n", pgmName, __LINE__, strerror( ENOMEM ) ); exit( 1 ); } /* ** Initialize the poll(2) array. */ for ( idx = 0; idx < descSize; idx++ ) { desc[ idx ].fd = idx < tScktSize ? tSckt[ idx ] : uSckt[ idx - tScktSize ]; desc[ idx ].events = POLLIN; desc[ idx ].revents = 0; } /* ** Main PIT server loop. Handles both TCP & UDP requests. This is ** an interative server, and all requests are handled directly within the ** main loop. */ while ( true ) /* Do forever. */ { /* ** Wait for activity on one of the sockets. The DO..WHILE construct is ** used to restart the system call in the event the process is ** interrupted by a signal. */ do { status = poll( desc, descSize, -1 /* Wait indefinitely for input. */ ); } while ( ( status < 0 ) && ( errno == EINTR ) ); CHK( status ); /* Check for a bona fide system call error. */ /* ** Get the current time. */ #if defined(Windows) LARGE_INTEGER freq,counter; double wintime,bigcounter; /* For Windows the time_of_day() is useless. It increments in 55 milli * second increments. By using the Win32api one can get access to the * high performance measurement interfaces. With this one can get back * into the 8 to 9 microsecond resolution. */ QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&counter); bigcounter=(double)counter.HighPart *(double)0xffffffff + (double)counter.LowPart; wintime = (double)(bigcounter/(double)freq.LowPart); secs = (long long)(wintime * 1000000); #else ret = gettimeofday( &tm,0 ); secs = ((unsigned long long)tm.tv_sec * 1000000) + (unsigned long long)tm.tv_usec; #endif ret = sprintf(timeStr,"%llu",secs); timeLen = strlen( timeStr ); /* ** Process sockets with input available. */ for ( idx = 0; idx < descSize; idx++ ) { switch ( desc[ idx ].revents ) { case 0: /* No activity on this socket; try the next. */ continue; case POLLIN: /* Network activity. Go process it. */ break; default: /* Invalid poll events. */ { fprintf( stderr, "%s (line %d): ERROR - Invalid poll event (0x%02X).\n", pgmName, __LINE__, desc[ idx ].revents ); exit( 1 ); } } /* End SWITCH on returned poll events. */ /* ** Determine if this is a TCP request or UDP request. */ if ( idx < tScktSize ) { /* ** TCP connection requested. Accept it. Notice the use of ** the sockaddr_storage data type. */ sadrLen = sizeof( sockStor ); sadr = (struct sockaddr*) &sockStor; CHK( newSckt = accept( desc[ idx ].fd, sadr, &sadrLen ) ); CHK( shutdown( newSckt, /* Server never recv's anything. */ SHUT_RD ) ); if ( verbose ) { /* ** Display the socket address of the remote client. Begin with ** the address-independent fields. */ fprintf( stderr, "Sockaddr info for new TCP client:\n" " sa_family = %d (AF_INET = %d, AF_INET6 = %d)\n" " addr len = %d (sockaddr_in = %lu, " "sockaddr_in6 = %lu)\n", sadr->sa_family, AF_INET, AF_INET6, sadrLen, sizeof( struct sockaddr_in ), sizeof( struct sockaddr_in6 ) ); /* ** Display the address-specific fields. */ getnameinfo( sadr, sadrLen, hostBfr, sizeof( hostBfr ), servBfr, sizeof( servBfr ), NI_NUMERICHOST | NI_NUMERICSERV ); /* ** Notice that we're switching on an address family now, not a ** protocol family. */ switch ( sadr->sa_family ) { case AF_INET: /* IPv4 address. */ { struct sockaddr_in *p = (struct sockaddr_in*) sadr; fprintf( stderr, " sin_addr = sin_family: %d\n" " sin_addr: %s\n" " sin_port: %s\n", p->sin_family, hostBfr, servBfr ); break; } /* End CASE of IPv4. */ case AF_INET6: /* IPv6 address. */ { struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; fprintf( stderr, " sin6_addr = sin6_family: %d\n" " sin6_addr: %s\n" " sin6_port: %s\n" " sin6_flowinfo: %d\n" " sin6_scope_id: %d\n", p->sin6_family, hostBfr, servBfr, p->sin6_flowinfo, p->sin6_scope_id ); break; } /* End CASE of IPv6. */ default: /* Can never get here, but for completeness. */ { fprintf( stderr, "%s (line %d): ERROR - Unknown address " "family (%d).\n", pgmName, __LINE__, sadr->sa_family ); break; } /* End DEFAULT case (unknown address family). */ } /* End SWITCH on address family. */ } /* End IF verbose mode. */ /* ** Send the PIT to the client. */ wBytes = timeLen; while ( wBytes > 0 ) { do { count = write( newSckt, timeStr, wBytes ); } while ( ( count < 0 ) && ( errno == EINTR ) ); CHK( count ); /* Check for an error. */ wBytes -= count; } /* End WHILE there is data to send. */ CHK( close( newSckt ) ); } /* End IF this was a TCP connection request. */ else { /* ** This is a UDP socket, and a datagram is available. The funny ** thing about UDP requests is that this server doesn't require any ** client input; but it can't send the PIT unless it knows a client ** wants the data, and the only way that can occur with UDP is if ** the server receives a datagram from the client. Thus, the ** server must receive _something_, but the content of the datagram ** is irrelevant. Read in the datagram. Again note the use of ** sockaddr_storage to receive the address. */ sadrLen = sizeof( sockStor ); sadr = (struct sockaddr*) &sockStor; CHK( count = recvfrom( desc[ idx ].fd, bfr, sizeof( bfr ), 0, sadr, &sadrLen ) ); /* ** Display whatever was received on stdout. */ if ( verbose ) { ssize_t rBytes = count; fprintf( stderr, "%s: UDP datagram received (%ld bytes).\n", pgmName, count ); while ( count > 0 ) { fputc( bfr[ rBytes - count-- ], stdout ); } if ( bfr[ rBytes-1 ] != '\n' ) fputc( '\n', stdout ); /* Newline also flushes stdout. */ /* ** Display the socket address of the remote client. Address- ** independent fields first. */ fprintf( stderr, "Remote client's sockaddr info:\n" " sa_family = %d (AF_INET = %d, AF_INET6 = %d)\n" " addr len = %d (sockaddr_in = %lu, " "sockaddr_in6 = %lu)\n", sadr->sa_family, AF_INET, AF_INET6, sadrLen, sizeof( struct sockaddr_in ), sizeof( struct sockaddr_in6 ) ); /* ** Display the address-specific information. */ getnameinfo( sadr, sadrLen, hostBfr, sizeof( hostBfr ), servBfr, sizeof( servBfr ), NI_NUMERICHOST | NI_NUMERICSERV ); switch ( sadr->sa_family ) { case AF_INET: /* IPv4 address. */ { struct sockaddr_in *p = (struct sockaddr_in*) sadr; fprintf( stderr, " sin_addr = sin_family: %d\n" " sin_addr: %s\n" " sin_port: %s\n", p->sin_family, hostBfr, servBfr ); break; } /* End CASE of IPv4 address. */ case AF_INET6: /* IPv6 address. */ { struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; fprintf( stderr, " sin6_addr = sin6_family: %d\n" " sin6_addr: %s\n" " sin6_port: %s\n" " sin6_flowinfo: %d\n" " sin6_scope_id: %d\n", p->sin6_family, hostBfr, servBfr, p->sin6_flowinfo, p->sin6_scope_id ); break; } /* End CASE of IPv6 address. */ default: /* Can never get here, but for completeness. */ { fprintf( stderr, "%s (line %d): ERROR - Unknown address " "family (%d).\n", pgmName, __LINE__, sadr->sa_family ); break; } /* End DEFAULT case (unknown address family). */ } /* End SWITCH on address family. */ } /* End IF verbose mode. */ /* ** Send the PIT to the client. */ wBytes = timeLen; while ( wBytes > 0 ) { do { count = sendto( desc[ idx ].fd, timeStr, wBytes, 0, sadr, /* Address & address length */ sadrLen ); /* received in recvfrom(). */ } while ( ( count < 0 ) && ( errno == EINTR ) ); CHK( count ); /* Check for a bona fide error. */ wBytes -= count; } /* End WHILE there is data to send. */ } /* End ELSE a UDP datagram is available. */ desc[ idx ].revents = 0; /* Clear the returned poll events. */ } /* End FOR each socket descriptor. */ } /* End WHILE forever. */ } /* End pit() */