/* **************************************************************** * * * Copyright (c) Digital Equipment Corporation, 1990, 1995,* * 1996 * ** M006 ** * All Rights Reserved. Unpublished rights reserved * * under the copyright laws of the United States. * * * * The software contained on this media is proprietary * * to and embodies the confidential technology of * * Digital Equipment Corporation. Possession, use, * * duplication or dissemination of the software and * * media is authorized only pursuant to a valid written * * license from Digital Equipment Corporation. * * * * RESTRICTED RIGHTS LEGEND Use, duplication, or * * disclosure by the U.S. Government is subject to * * restrictions as set forth in Subparagraph (c)(1)(ii) * * of DFARS 252.227-7013, or in FAR 52.227-19, as * * applicable. * * * **************************************************************** */ /* ************************************************************************* * * Revision History * ************************************************************************* * * Edit# Date By Description * ----- ------------ ---- ---------------------------------- * * 01 17-Dec-1990 Michael Daniele Created. * 1-Aug-1992 J L modified * 07-Feb-1994 V Depagne modified * T1.4.0 Sept-1994 F. Amrani Back porting from OSF to VMS/ULTRIX * T1.4.1 Nov-1994 F. Amrani MCC_FT_QAR_140 #12 * Redesign to allow mcc_evc_send * to support dual lan operation * V1.4-0 Dec-1994 F.Amrani MCC_MAINT QAR 254 * the return_code 0 of * close() means a successful completion * (and not MCC_S_NORMAL) * V1.4-n 27-Sep-1994 B. Durif Add include cma.h * * 006 04-Mar-1996 Eric Beroud X1.5-000 - Porting to ALPHA ** A006 ** * Suppress "fd_set" definitions if ** A006 ** * incuded by socket.h ( From DECC ** A006 ** * V5.3) ** A006 ** * * ************************************************************************* */ /* ************************************************************************* * * mcc_evc_api_udpip.c * * This module contains native routines for TCP/IP network communications. * * It currently supports UDP. * * The mcc_evc_sink and mcc_evc_api functions call these core functions. * * All functions return a condition value of 1 for True, or 0 for False. * All functions accept as their first 2 arguments a pointer to a * ( previously allocated ) context block, and a pointer to a longword * in which is passed a status value from the underlying network software * ( usually the value of errno, etc. ) A function return of False, but * with the return code = 0 means an unimplemented or illegal request. * ************************************************************************* */ #include #include #ifdef __DECC #include /* QAR.351 */ #endif #if _MCC_OS_ == _MCC__VMS #include #include #include #include #include #else #include #include #include #include #include #include #endif #include #include "mcc_evc_api_def.h" #include "mcc_evc_api_udpip.h" #include "inet_io_def.h" /* Default port to use for Data Collector */ #define EVC_UDP_PORT_DEF 1630 #if _MCC_OS_ == _MCC__VMS # ifndef __FD_SET 1 /* Now defined in socket.h */ /* A006 */ /* *------------------------- cut and pasted from ultrix types.h -------------------- * The maximum number of file descriptors is now a configurable option * (max_nofile variable in /sys/conf/{mips|vax}/param.c). * The getdtablesize(2) system call should be used to obtain the * current limit. The value returned by getdtablesize() must be greater * than 64, and less than or equal to MAX_NOFILE in types.h . The * MAX_NOFILE define is needed for backward compatability with broken * programs that need a static sized array for selecting. These programs * should be modified to use the getdtablesize() interface for sizing. */ #define MAX_NOFILE 4096 /* This should not exist ! */ #define NBBY 8 /* number of bits in a byte */ /* * Select uses bit masks of file descriptors in longs. * These macros manipulate such bit fields (the filesystem macros use chars). * FD_SETSIZE may be defined by the user, but the default here * should be >= NOFILE (param.h). */ # ifndef FD_SETSIZE # define FD_SETSIZE MAX_NOFILE # endif /* How many things we'll allow select to use. 0 if unlimited */ #define MAXSELFD MAX_NOFILE typedef MCC_T_Unsigned32 fd_mask; #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask (power of 2!)*/ #define NFDSHIFT 5 /* Shift based on above */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif /* howmany */ typedef struct fd_set { fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset((MCC_T_CHAR *)(p), (char)0,sizeof(*(p))) /*------------------------- cut and pasted from ultrix types.h --------------------*/ # endif /* A006 */ #define MAX_FDS_INTS 1 #else #define MAX_FDS_INTS 32 #endif #define MAX_FDS_BYTES (MAX_FDS_INTS*4) #define MAX_FDS_BITS (MAX_FDS_BYTES*8) /* external function prototypes*/ #if _MCC_OS_==_MCC__UNIX int select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) ; int gethostname ( char *address, int address_len ); #endif /************************************************************************* * * Name: mcc_tcpip_IO_init * * Description: Initializes Internet communications. * Creates ( and optionally binds ) a socket. * Inits the IO context block. * * Input Params: * * IO_Context Pointer to a handle context block * * IO_Class The class ( socket ) type * * IO_Address The address to associate with this socket. * 0 is interpreted as ALL ( for a server ) * (network format) * IO_Port The port to associate with this socket * 0 is interpreted as a randomly assigned port * ( as for UDP clients ) (host format) * Output Params: * * IO_Return Pointer to longword, updated with return * status information * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_init ( struct mcc_tcpip_IO_int_ctx *IO_Context, mcc_tcpip_IO_ClassType IO_Class, MCC_T_Unsigned32 *IO_Address, mcc_tcpip_IO_PortType IO_Port) { MCC_T_Integer32 Socket = -1; MCC_T_CVR status = MCC_S_NORMAL; MCC_T_Integer32 IntStatus; MCC_T_Integer32 Index = 0; MCC_T_Integer32 commtype ; MCC_T_Integer32 protocol ; /* *************************************************************** * * Create a socket. * *************************************************************** */ switch (IO_Class) { case UDP_IP : commtype = SOCK_DGRAM; protocol = IPPROTO_IP; break; case ICMP_IP : commtype = SOCK_RAW; protocol = IPPROTO_ICMP; break; default : return(MCC_S_FAILED); } #ifndef NOT_MCC TRY { Socket = socket( AF_INET, commtype , protocol ); } CATCH (cma_e_alerted) { status = MCC_S_ALERT_TERMREQ; } ENDTRY /* Send back any CMA alerts. */ if (status != MCC_S_NORMAL) return( status ); #else Socket = socket( AF_INET, commtype , protocol ); #endif if( Socket == -1 ) { return( MCC_S_FAILED ); } IO_Context->socket = Socket; if ( IO_Class == ICMP_IP ) return( status ); /* *************************************************************** * * Set up the local address/port information for this socket. * If port = 0, the network software will assign a random number. * If it's not 0, bind the address and port # to this socket. * *************************************************************** */ IO_Context->local_sockaddr.sin_family = AF_INET; IO_Context->local_sockaddr.sin_port = htons( IO_Port ); if( IO_Address == 0 ) IO_Context->local_sockaddr.sin_addr.s_addr = 0; else IO_Context->local_sockaddr.sin_addr.s_addr = *IO_Address; while( Index < 8 ) IO_Context->local_sockaddr.sin_zero[ Index++ ] = (MCC_T_CHAR) 0; if( IO_Port != 0 ) { IntStatus = bind( IO_Context->socket, ( struct sockaddr *)&IO_Context->local_sockaddr, sizeof( IO_Context->local_sockaddr ) ); if( IntStatus == -1 ) { return( MCC_S_FAILED ); } } /* *************************************************************** * * Set up the static fields in the remote address/port * structure. The actual address and port will be specified * in subsequent WRITE calls. * *************************************************************** */ IO_Context->remote_sockaddr.sin_family = AF_INET; Index = 0; while( Index < 8 ) IO_Context->remote_sockaddr.sin_zero[ Index++ ] = (MCC_T_CHAR)0; return( status ); } /* end tcpip_io_init */ /************************************************************************* * * Name: mcc_tcpip_IO_write * * Description: Write a buffer over TCP/IP. * * * * Input Params: * * IO_Context Pointer to a handle context block * * IO_Address The address to associate with this socket. * 0 is interpreted as ALL ( for a server ) * * IO_Port The port to associate with this socket * 0 is interpreted as a randomly assigned port * ( as for UDP clients ) * * Output Params: * * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_write( struct mcc_tcpip_IO_int_ctx *IO_Context , MCC_T_CHAR *IO_Buff , MCC_T_Unsigned32 *IO_Size , MCC_T_Unsigned32 *IO_Address , mcc_tcpip_IO_PortType IO_Port) { MCC_T_Integer32 Bytes_Sent; MCC_T_CVR status = MCC_S_NORMAL; /* *************************************************************** * * Finish loading the remote sockaddr data. * *************************************************************** */ IO_Context->remote_sockaddr.sin_port = htons( IO_Port ); IO_Context->remote_sockaddr.sin_addr.s_addr = *IO_Address; /* *************************************************************** * * Write the data and package up the correct return args. * *************************************************************** */ #ifndef NOT_MCC TRY { Bytes_Sent = sendto( IO_Context->socket, IO_Buff, *IO_Size, 0, ( struct sockaddr *)&IO_Context->remote_sockaddr, sizeof( IO_Context->remote_sockaddr ) ); } CATCH (cma_e_alerted) { status = MCC_S_ALERT_TERMREQ; } ENDTRY /* Send back any CMA alerts. */ if (status != MCC_S_NORMAL) return( status ); #else Bytes_Sent = sendto( IO_Context->socket, IO_Buff, *IO_Size, 0, ( struct sockaddr *)&IO_Context->remote_sockaddr, sizeof( IO_Context->remote_sockaddr ) ); #endif if( Bytes_Sent == -1 ) status = MCC_S_FAILED; else { if( Bytes_Sent != *IO_Size ) { *IO_Size = Bytes_Sent; status = MCC_S_FAILED; } } return( status ); } /* end tcpip_io_write */ /************************************************************************* * * Name: mcc_tcpip_IO_read * * Description: Read a buffer over TCP/IP. * * * * Input Params: * * IO_Context Pointer to a handle context block * * IO_Buff Address of buffer to load * * IO_Size Pointer to buff size [updated with bytes read ] * * IO_Timer number of seconds to wait before timing out. * * Output Params: * * IO_Address The IP address of the writer * * IO_Port The port of the writer * * * * Returns MCC_T_CVR MCC_S_NRMAL/MCC_S_FAILED/MCC_S_ALERT_TERMREQ * * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_read ( struct mcc_tcpip_IO_int_ctx * IO_Context , MCC_T_CHAR * IO_Buff , MCC_T_Unsigned32 * IO_Size , MCC_T_Unsigned32 IO_Timer , MCC_T_Unsigned32 * IO_Address , mcc_tcpip_IO_PortType * IO_Port) { MCC_T_Unsigned32 Length, Pending_Sockets; MCC_T_Integer32 Bytes_Rcvd; fd_set fds_ints; struct sockaddr_in Current_Sockaddr; struct timeval Select_Timer; MCC_T_CVR status = MCC_S_NORMAL; /* *************************************************************** * * If this is a read w/ timeout, set up and issue select() * on this socket only. * * In case of a timeout, set the return status to TRUE, but the * byte counter to 0. * *************************************************************** */ if( IO_Timer != 0 ) { Select_Timer.tv_sec = IO_Timer; Select_Timer.tv_usec = 0; FD_ZERO(&fds_ints); FD_SET( IO_Context->socket, &fds_ints ); #ifndef NOT_MCC TRY { Pending_Sockets = select( FD_SETSIZE, &fds_ints, NULL, NULL, &Select_Timer ); } CATCH (cma_e_alerted) { /* We were alerted. Return TRUE with a value (cma alerted) in IO_Return. The calling routine must check the IO_Return value whenever TRUE is returned. */ status = MCC_S_ALERT_TERMREQ; } ENDTRY /* Send back any CMA alerts. */ if( status != MCC_S_NORMAL) return( status ); #else Pending_Sockets = select( FD_SETSIZE, &fds_ints, NULL, NULL, &Select_Timer ); #endif switch( Pending_Sockets ) { case 0 : /* Timeout */ *IO_Size = 0; return( MCC_S_NORMAL ); case -1 : /* Error during select() */ return( MCC_S_FAILED ); default : /* Data is ready, fall thru */ ; } /* end of switch() */ } /* end of IO_timer != 0 */ /* *************************************************************** * * Read data from this socket. Update the bytes received, and * the peer IP address and port. * *************************************************************** */ Length = sizeof( Current_Sockaddr ); #ifndef NOT_MCC TRY { Bytes_Rcvd = recvfrom( IO_Context->socket, IO_Buff, *IO_Size, 0, (struct sockaddr *)&Current_Sockaddr, (int *)&Length ); } CATCH (cma_e_alerted) { /* We were alerted. Return TRUE with a value (cma alerted) in IO_Return. The calling routine must check the IO_Return value whenever TRUE is returned. */ status = MCC_S_ALERT_TERMREQ; } ENDTRY /* Send back any CMA alerts. */ if( status != MCC_S_NORMAL) return( status ); #else Bytes_Rcvd = recvfrom( IO_Context->socket, IO_Buff, *IO_Size, 0, (struct sockaddr *)&Current_Sockaddr, (int *)&Length ); #endif if( Bytes_Rcvd == -1 ) { return( MCC_S_FAILED ); } else { *IO_Size = Bytes_Rcvd; *IO_Address = Current_Sockaddr.sin_addr.s_addr; *IO_Port = htons( Current_Sockaddr.sin_port ); return( status ); } } /* end tcpip_io_read */ /************************************************************************* * * Name: mcc_tcpip_IO_term * * Description: Terminate TCP/IP communication * * * * Input Params: * * IO_Context Pointer to a handle context block * * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_term( struct mcc_tcpip_IO_int_ctx * IO_Context ) { MCC_T_Integer32 status =0; if( IO_Context->socket != 0 ) { #ifndef NOT_MCC TRY { status = close( IO_Context->socket ); } CATCH (cma_e_alerted) { /* We were alerted. Return TRUE with a value (cma alerted) in IO_Return. The calling routine must check the IO_Return value whenever TRUE is returned. */ status = MCC_S_ALERT_TERMREQ; } ENDTRY /* Send back any CMA alerts. */ /**** BEGIN-MCC_MAINT_QAR_254-FA-122294 ****/ if( status == MCC_S_ALERT_TERMREQ) /**** END-MCC_MAINT_QAR_254-FA-122294 ****/ return( status ); #else status = close( IO_Context->socket ); #endif if (status == -1) { return( MCC_S_FAILED ); } } return( TRUE ); } /* end tcpip_io_term */ /************************************************************************* * * Name: mcc_tcpip_IO_host * * Description: Translate a host name into IP address(es) * * * * Params: * * IO_Host Pointer to null terminated host name string * * IO_Address Address of array to store of IP addresses * * IO_Numaddr Pointer to number of array elements, updated * with number of addresses returned * * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_host( MCC_T_CHAR * IO_Host , MCC_T_Unsigned32 *IO_Address , MCC_T_Unsigned32 * IO_Numaddr) { MCC_T_CVR status = MCC_S_NORMAL; MCC_T_Integer32 Index = 0; MCC_T_Unsigned32 **Ptr; struct hostent *p_host; /* netdb.h */ /* *************************************************************** * * Translate the host name into a hostent structure. * If name is not known, set the number of addresses to 0. * *************************************************************** */ #ifndef NOT_MCC TRY { p_host = gethostbyname( IO_Host ); } CATCH (cma_e_alerted) { /* We were alerted. Return TRUE with a value (cma alerted) in IO_Return. The calling routine must check the IO_Return value if TRUE is returned. */ status = MCC_S_ALERT_TERMREQ; } ENDTRY #else p_host = gethostbyname( IO_Host ); #endif if ( p_host == MCC_K_NULL_PTR ) { *IO_Numaddr = 0; return( MCC_S_FAILED ); } /* *************************************************************** * * Load the address array with as many addresses as are associated * with this host, or IO_Numaddr, whichever is less. But set * IO_Numaddr to the number there actually are. * *************************************************************** */ Ptr = (MCC_T_Unsigned32**)p_host->h_addr_list; while( *Ptr != MCC_K_NULL_PTR ) { /**** BEGIN-MCC_FT_QAR_140_#12-FA-112894 ****/ if (( Index < *IO_Numaddr ) || (*IO_Numaddr == 0)) IO_Address[ Index ] = **Ptr; /**** END-MCC_FT_QAR_140_#12-FA-112894 ****/ Index++; Ptr++; } *IO_Numaddr = Index; return( status ); } /* end tcpip_io_host */ /************************************************************************* * * Name: mcc_tcpip_IO_addr * * Description: Translate an IP address into host name(s) * * Params: * * IO_Return Pointer to longword, updated with return * status information * * IO_Host Pointer to buffer for host name * * IO_Address The IP address * * IO_Size Pointer to size of buffer, updated with * size of host name. If no translation, = 0 * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_addr (MCC_T_CHAR *IO_Host, MCC_T_Unsigned32 *IO_Address, MCC_T_Unsigned32 *IO_Size ) { MCC_T_CVR status = MCC_S_NORMAL; MCC_T_Integer32 Index = 0; struct hostent *p_host; /* netdb.h */ MCC_T_BYTE *addr ; /* *************************************************************** * * Translate the IP address into a hostent structure. * If address is not known, set the host size to 0. * If known, copy as much as possible to buffer, null-terminate * if possible. Return the ACTUAL host name size. * *************************************************************** */ addr = (MCC_T_BYTE *)IO_Address; #ifndef NOT_MCC TRY { p_host = gethostbyaddr( IO_Address, sizeof( IO_Address ), AF_INET ); } CATCH (cma_e_alerted) { /* We were alerted. Return TRUE with a value (cma alerted) in IO_Return. The calling routine must check the IO_Return value if TRUE is returned. */ status = MCC_S_ALERT_TERMREQ; } ENDTRY #else p_host = gethostbyaddr( IO_Address, sizeof( IO_Address ), AF_INET ); #endif if (( p_host == MCC_K_NULL_PTR ) || ( status == MCC_S_ALERT_TERMREQ )) { *IO_Size = 0; status = MCC_S_FAILED; } else { *IO_Size = strlen( p_host->h_name ); strncpy( IO_Host, p_host->h_name, *IO_Size ); } return( status ); } /* end tcpip_io_addr */ /************************************************************************* * * Name: mcc_tcpip_IO_me * * Description: Get the host name and IP address(es) of the local system. * * Params: * * IO_Host Pointer to buffer to hold host name, which is * truncated or null terminated * * IO_Size Pointer to size of above buffer, updated with * actual size of host name * * IO_Address Address of array to store of IP addresses * * IO_Numaddr Pointer to number of array elements, updated * with number of addresses returned * * Returns MCC_T_CVR MCC_S_NORMAL/MCC_S_FAILED * ************************************************************************* */ MCC_T_CVR mcc_tcpip_IO_me ( MCC_T_CHAR *IO_HostName, MCC_T_Unsigned32 *IO_Size, MCC_T_Unsigned32 *IO_Address, MCC_T_Unsigned32 *IO_Numaddr) { MCC_T_Integer32 status = MCC_S_NORMAL; /* *************************************************************** * * Get the local host primary name. Update size on success. * *************************************************************** */ if( gethostname( IO_HostName, *IO_Size ) == -1 ) { return( MCC_S_FAILED ); } else *IO_Size = strlen( IO_HostName ); /* *************************************************************** * * Go ahead and translate whatever we got. * *************************************************************** */ status = mcc_tcpip_IO_host( IO_HostName , IO_Address, IO_Numaddr ); return (status); } /* end tcpip_io_me */