/*
 * universal multiplayer library level 0 implementation file
 * transport layer: UDP PC-windows 95
 */
 
/*
 *      Author: Christophe Roguet after Benoit GERMAIN
 *
 *      Modification Date:      24/04/96 first draft : I consider that the datagram delivery is completely reliable ...
 *                                                                                         which might be right on a local ethernet with low load and small datagrams...
 */
 
#include "warnings.h"
 
#if defined(_MSC_VER)
  #pragma warning(disable : 4306)
#endif
 
#include "NetSock.h"
 
/* 
 * we need this one to fill the interface structure 
 * with pointers to the correct functions 
 */
#include "Privl0UDP.h"
#include "PrivNetDef.h"
#include "L0GlDef.h"
#include "NetMemCo.h"
#include "NetEnd.h"
#include <NET\NetDebug.h>
 
/* array of descriptors to access a physical channel. */
static tdstL0PCWin95UDPChannel gs_a_stL0PCWin95UDPChannels[C_ucL0MaxNumberOfUDPChannels];
 
/* the IP adress of the computer the application is running on, in network order */
static struct in_addr gs_stMyAddress;
 
/* the global socket descriptor used for all net operations */
static int gs_iSD;
static unsigned short gs_uwNumberOfPort;
 
static char gs_bL0PCWin95UDPInitState;
 
static tdstWinSockFunc *gs_p_stUDPWinSocDesc;
static char gs_cIsWinsocDescInit = 0;
 
#define DLL_FD_ISSET(fd, set) ((gs_p_stUDPWinSocDesc->m_pfn_i_WSAFDIsSet)((SOCKET)(fd), (fd_set *)(set)))
/*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPGetChannel
 *
 * look up the channel associated to a particular remote address
 *
 *****************************************************************************
 *
 * Input:   inet address to look up
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date: June 6, 1996                    Author: Christophe ROGUET
 * take the 'broadcast channel' flag into account, in order to be able to use
 * the library with a direct connection over internet
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPGetChannel(struct sockaddr_in *Key)
{
 tduwNetChannel uwCurrentChannel;
 for(uwCurrentChannel=0; uwCurrentChannel<C_ucL0MaxNumberOfUDPChannels; uwCurrentChannel++)
  if(
  gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse==1
  && !gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsBroadcast
  &&  gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].stRemoteAddr.sin_addr.s_addr==Key->sin_addr.s_addr
  )
   return uwCurrentChannel;
 
 return C_uwNetInvalidChannel;      /* Error invalid channel ?*/
}
 
 /*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPOpenChannel
 *
 * returns the identification number of an unused slot in the channel table
 *
 *****************************************************************************
 *
 * Input:   inet address to bind to the channel,
 *          port number in a net format
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
static tduwNetChannel uwL0PCWin95UDPOpenChannel(struct sockaddr_in *pRemoteAddr,unsigned short uwPortNumber)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the channel table until we find an unused slot */
 for(uwCurrentChannel=0; 
     uwCurrentChannel<C_ucL0MaxNumberOfUDPChannels; 
     uwCurrentChannel++)
  if(gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse == 0)
  {
   /* set the use mark */
   gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse =1;
   /* set the remote address */
   g_pfn_vNetMemcpy
    (
    &gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].stRemoteAddr.sin_addr,
    &pRemoteAddr->sin_addr,
    sizeof (pRemoteAddr->sin_addr)
    );
   /* Sets the port :*/
   gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].stRemoteAddr.sin_port=uwPortNumber;
   /* set the ring to empty */
   
   gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].p_FirstDataCell=
    (tdstNetDataCell *)C_pNull;
   return uwCurrentChannel;
  }
 
 /* there are no free channels */
 return C_uwNetInvalidChannel;      /* Error invalid channel ?*/
}
 
/*****************************************************************************
 * 
 * Description: vL0PCWin95UDPCloseChannel
 *
 * frees a slot in the channel table
 *
 *****************************************************************************
 *
 * Input:     identifier of the channel
 *
 * Output:        none
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
static void vL0PCWin95UDPCloseChannel(tduwNetChannel uwChannel)
{
 tdstNetDataCell *p_DataCellNext;
 tdstNetDataCell *p_DataCellCur;
 
 /* broadcast channels should never be closed */
 if(gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsBroadcast)
  return;
 /* clear the use mark */
 gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsSlotInUse=0;
 /* set the destination adress to none (let's be cautious) */
 gs_a_stL0PCWin95UDPChannels[uwChannel].stRemoteAddr.sin_addr.s_addr=
  gs_p_stUDPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE);
 p_DataCellCur = p_DataCellNext = 
  gs_a_stL0PCWin95UDPChannels[uwChannel].p_FirstDataCell;
 
 while(p_DataCellNext/*(tdstNetMessageCell *)!=()C_pNull*/)
 {
  p_DataCellNext = p_DataCellCur->p_stNextDataCell;
  vFree((tdpPointer)p_DataCellCur->pData);
  vFree((tdpPointer)p_DataCellCur);
 }
}
 
 /*****************************************************************************
 * 
 * Description: eL0PCWin95UDPInsertMessage
 *
 * insert a message at the end of a channel's queue
 *
 *****************************************************************************
 *
 * Input:   channel identifier, pointer to a message
 *
 * Output:  error status
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPInsertMessage(tduwNetChannel uwChannel, tdpPointer pData)
{
 tdstNetDataCell *p_DataCellNew;
 
 p_DataCellNew = (tdstNetDataCell*)pMalloc(sizeof(tdstNetDataCell));
 /* are we out of space ? */
 if(!p_DataCellNew)
  return NetLib_E_es_NotEnoughMemory;/* error: the ring is full */
 
 /* no, store the message at the head of the ring */
 p_DataCellNew->pData =pData;
 p_DataCellNew->p_stNextDataCell = 
 gs_a_stL0PCWin95UDPChannels[uwChannel].p_FirstDataCell;
 gs_a_stL0PCWin95UDPChannels[uwChannel].p_FirstDataCell =
  p_DataCellNew;
 
 return NetLib_E_es_NoError; /* no error */
}
 
 /*****************************************************************************
 * 
 * Description: eL0PCWin95UDPRemoveMessage
 *
 * remove the first message of a channel's queue
 *
 *****************************************************************************
 *
 * Input:   channel identifier, address of a pointer to a message
 *
 * Output:  error status
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPRemoveMessage(tduwNetChannel uwChannel, tdpPointer *ppData)
{
 tdstL0PCWin95UDPChannel *p_stChSlot; /* a reference to the Channel slot */
 tdstNetDataCell *p_DataCellCur;
 tdstNetDataCell *p_DataCellPrev;
  
 p_stChSlot=&gs_a_stL0PCWin95UDPChannels[uwChannel];
 /* is there anything in the ring ? */
 if(!p_stChSlot->p_FirstDataCell)
  return NetLib_E_es_FIFOIsEmpty; /* error: the ring is empty */
 
 /* yes, return message at the tail of the ring */
 p_DataCellPrev = p_DataCellCur = p_stChSlot->p_FirstDataCell;
 while(p_DataCellCur->p_stNextDataCell)
 {
  p_DataCellPrev = p_DataCellCur;
  p_DataCellCur = p_DataCellCur->p_stNextDataCell;
 }
 
 *ppData= p_DataCellCur->pData;
 if(p_stChSlot->p_FirstDataCell != p_DataCellCur )
 {
  vFree((tdpPointer)p_DataCellCur);
  p_DataCellPrev->p_stNextDataCell = (tdstNetDataCell*)C_pNull;
 }
 else
  p_stChSlot->p_FirstDataCell = (tdstNetDataCell*)C_pNull;
 
 return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * 
 * Description: eL0PCWin95UDPMessageAvailable
 *
 * test the availability of a message on the socket
 *
 *****************************************************************************
 *
 * Input:     none
 *
 * Output:        
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPMessageAvailable(void)
{
 static fd_set readfds;
 static struct timeval timeout = { 0, 0 };
 int ok;
 
 FD_ZERO(&readfds);
 FD_SET(gs_iSD, &readfds);
 
 if((ok=gs_p_stUDPWinSocDesc->m_pfn_i_select(0, &readfds, NULL, NULL, &timeout))==SOCKET_ERROR)
 {
  return NetLib_E_es_SelectSocketFailure;
 }
 
 /* return TRUE iff there's something waiting to be read from our socket*/
 if(ok!=0 && DLL_FD_ISSET(gs_iSD, &readfds))
  return NetLib_E_es_True;
 else
  return NetLib_E_es_False;
}
 
/*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPGetMessage
 *
 * Get one message from the socket and insert it in a channel
 *
 *****************************************************************************
 *
 * Input:  none
 *
 * Output: identifier of the channel associated to the remote end from which the message originates
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log: Adding check sum information
 *
 * Date: July 9,1996                     Author: Albert Pais
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPGetMessage(void)
{
 struct sockaddr_in s_stExpAddr; /* address of the expeditor of the received message */
 int iNameLength;                                /* length of the above structure */
 tdstNetMessage stMsgHeader;                /* header of the incoming message */
 unsigned long ulMsgSize;                /* length of the incoming message (header included) */ 
 tduwNetChannel uwChannel;                  /* channel in which to insert the incoming message */
 tduwNetChannel uwReturnChannel;    /* channel number to return */
 tdpPointer pData;                               /* address of the block where the incoming message gets copied */
 unsigned char ucLoopbackMessage;                        /* does the incoming message come from us */
 
 pData=NULL;
 iNameLength = sizeof s_stExpAddr;
 do
 {
  /* is there anything to read from the socket ? */
  if(eL0PCWin95UDPMessageAvailable()!=NetLib_E_es_True)
   return C_uwNetInvalidChannel; /* error: no message available */
 
  if(gs_p_stUDPWinSocDesc->m_pfn_i_recvfrom(gs_iSD, (char *)&stMsgHeader, sizeof(stMsgHeader), MSG_PEEK,
             (struct sockaddr *)&s_stExpAddr,&iNameLength)
     ==SOCKET_ERROR)
  {
   if(gs_p_stUDPWinSocDesc->m_pfn_i_WSAGetLastError()!=WSAEMSGSIZE)
   {
    return C_uwNetInvalidChannel;
   }
  }
 
    /* if big/little endian differs, swap the header before anything else*/
  if (stMsgHeader.uxHeadBigEndian!=NetLib_ucGetLittleBigEndian())
    NetLib_eSwapLittleBigEndianMsgHeader(&stMsgHeader);
 
  /* check if the header is correct :*/
  if
   (
   (M_uwCheckSumSlot((&stMsgHeader)) != M_uwProcessCheckSum((&stMsgHeader)))||
   (stMsgHeader.eMessageType <= E_Net_mt_InvalidMessage)||
   (stMsgHeader.eMessageType >= E_Net_mt_LastSysMsg)
   )
  {/* the message type is not correct :*/
   /* Purge the message : */
   gs_p_stUDPWinSocDesc->m_pfn_i_recvfrom(gs_iSD, (char*)&stMsgHeader, 0, 0,
            (struct sockaddr *)&s_stExpAddr, &iNameLength);
   /* Return en invalid channel :*/
   return C_uwNetInvalidChannel;
  }
                
  /* allocate a block of the size of the incoming message */
  ulMsgSize=stMsgHeader.uwMessageSizeInBytes + sizeof(tdstNetMessage);
  pData=pMalloc(ulMsgSize);
  
  if(pData == C_pNull)
  {/* the pointer couldn't be allocated :*/
   /* Purge the message : */
   gs_p_stUDPWinSocDesc->m_pfn_i_recvfrom(gs_iSD, (char*)&stMsgHeader, 0, 0,
        (struct sockaddr *)&s_stExpAddr, &iNameLength);
   /* Return en invalid channel :*/
   return C_uwNetInvalidChannel;
  }
 
  /* read the message into this block */
  if(gs_p_stUDPWinSocDesc->m_pfn_i_recvfrom(gs_iSD, pData, ulMsgSize, 0,
   (struct sockaddr *)&s_stExpAddr, &iNameLength)==SOCKET_ERROR)
  {
   return C_uwNetInvalidChannel;
  }
#if defined(NET_USE_DEBUG)
    vDebugSISISI(Net_C_Debug_UDP,"Receive Type",stMsgHeader.eMessageType,
                 "Size",stMsgHeader.uwMessageSizeInBytes,
                 "Source",stMsgHeader.uxSenderId);
#endif /* NET_USE_DEBUG */
 
  /* if the message originates from the host, discard it */
  ucLoopbackMessage = s_stExpAddr.sin_addr.s_addr==gs_stMyAddress.s_addr;
  if(ucLoopbackMessage)
   vFree(pData);
  /* loop until we read a 'real' message */
 } while(ucLoopbackMessage);
 
 /* does the message come from an already registered channel ? */
 
 uwChannel=uwL0PCWin95UDPGetChannel(&s_stExpAddr);
 
 if(uwChannel==C_uwNetInvalidChannel)
 {
  /* no, allocate a new channel */
  uwChannel=uwL0PCWin95UDPOpenChannel(&s_stExpAddr,gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_port);
 
  /* remember that this channel is new: insert its number into the broadcast channel's queue */
  uwReturnChannel=0; /* zero is the broadcast channel's number */
  eL0PCWin95UDPInsertMessage(uwReturnChannel, (tdpPointer)uwChannel);
 
  /* mark the new channel as not readable */
  gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsReadable=0;
 }
 else
  uwReturnChannel=uwChannel;
 
 /* insert the message into the associated channel's queue */
 
 if(eL0PCWin95UDPInsertMessage(uwChannel, pData)==NetLib_E_es_FIFOIsFull)
  return C_uwNetInvalidChannel;
 
 /* return the channel number associated to the incoming message                                                                          */
 /* i.e. : - if the remote address wasn't already registered, return the broadcast channel's number   */
 /*                - if it was, return the channel number of the queue in which the message has been inserted */
 return uwReturnChannel;
}
 
 /*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPStartChannelScan
 *
 * returns the identification number of the first channel for the protocol
 *
 *****************************************************************************
 *
 * Input:   none
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPStartChannelScan(void)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the array of channel definitions */
 for (uwCurrentChannel = 0; uwCurrentChannel < C_ucL0MaxNumberOfUDPChannels; uwCurrentChannel ++)
 /* if the slot is used, and can transmit data to another individual player */ 
 if 
 (
     gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsReadable
 )
  /* return its index value */
  return uwCurrentChannel;
 
 /* else no slot is in use */
 return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPNextChannel
 *
 * returns the identification number of the channel following the last
 *
 *****************************************************************************
 *
 * Input:   uwLastScannedChannel, index returned by the previous call
 *
 * Output:  identification of the channel following the specified one
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPNextChannel(tduwNetChannel uwLastScannedChannel)
{
 tduwNetChannel uwCurrentChannel;
 
 /* 
  * scan the array of channel definitions, starting with the channel following
  * the last channel returned
  */
 for 
 (
  uwCurrentChannel = uwLastScannedChannel + 1; 
  uwCurrentChannel < C_ucL0MaxNumberOfUDPChannels; 
  uwCurrentChannel ++
 )
 /* if the slot is used, and can transmit data to another individual player */ 
 if 
 (
     gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse 
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsReadable
 )
  /* return its index value */
  return uwCurrentChannel;
 
 /* else no slot is in use */
 return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPStartBroadcastChannelScan
 *
 * returns the identification number of the first broadcast channel
 * for the protocol
 *
 *****************************************************************************
 *
 * Input:   none
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPStartBroadcastChannelScan(void)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the array of channel definitions */
 for (uwCurrentChannel = 0; uwCurrentChannel < C_ucL0MaxNumberOfUDPChannels; uwCurrentChannel ++)
 /* if the slot is used, and the channel can send broadcast data */ 
 if 
 (
     gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse 
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsBroadcast
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsReadable
 )
  /* return its index value */
  return uwCurrentChannel;
 
 /* else no slot is in use */
 return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * 
 * Description: uwL0PCWin95UDPNextBroadcastChannel
 *
 * returns the identification number of the broadcast channel following
 * the specified one
 *
 *****************************************************************************
 *
 * Input:   uwLastScannedChannel, index returned by the previous call
 *
 * Output:  identification of the channel following the specified one
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95UDPNextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
 tduwNetChannel uwCurrentChannel;
 
 /* 
  * scan the array of channel definitions, starting with the channel following
  * the last channel returned
  */
 for 
 (
  uwCurrentChannel = uwLastScannedChannel + 1; 
  uwCurrentChannel < C_ucL0MaxNumberOfUDPChannels; 
  uwCurrentChannel ++
 )
 /* if the slot is used, and can transmit broadcast data */ 
 if 
 (
     gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsSlotInUse 
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsBroadcast
  && gs_a_stL0PCWin95UDPChannels[uwCurrentChannel].ubf1IsReadable
 )
  /* return its index value */
  return uwCurrentChannel;
 
 /* else no slot is in use */
 return C_uwNetInvalidChannel;
}
 
 
/*****************************************************************************
 * 
 * Description: eL0PCWin95UDPReadData
 *
 *
 *****************************************************************************
 *
 * Input:   p_uwChannel, pointer on a field to store the identification of
 *          a new channel in case of access to a newly created broacast channel
 *          (irrelevant for the serial layer)
 *          ppData, address of the pointer to retrieve a full message if
 *          necessary.
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData)
{
 tduwNetChannel uwChannel;
 tdpPointer pToBeCast;
 
 /* verify the validity of the Channel */
 if(*p_uwChannel>=C_ucL0MaxNumberOfUDPChannels)
  return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
 if(!gs_a_stL0PCWin95UDPChannels[*p_uwChannel].ubf1IsSlotInUse)
  return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
 if(!gs_a_stL0PCWin95UDPChannels[*p_uwChannel].ubf1IsReadable)
  /* is it worth a new error code ? */
  return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
 /* differenciate broadcast channel and normal channels */
 
 if(gs_a_stL0PCWin95UDPChannels[*p_uwChannel].ubf1IsBroadcast)
 {
  /* is there already a message in the broadcast queue ? */
  if(eL0PCWin95UDPRemoveMessage(*p_uwChannel, &pToBeCast)==NetLib_E_es_FIFOIsEmpty)
  {
   /* no : try to get a message from the socket */
   do
   {
    /* read the first message available from the socket */
    uwChannel=uwL0PCWin95UDPGetMessage();
    /* until we get a message for the broadcast channel */
   } while(uwChannel != *p_uwChannel && uwChannel != C_uwNetInvalidChannel);
 
   if(uwChannel==C_uwNetInvalidChannel)
    return NetLib_E_es_FIFOIsEmpty;    /* error: no message available */
 
   eL0PCWin95UDPRemoveMessage(uwChannel, &pToBeCast); /* normally this call produces no error */
  }
 
  uwChannel=(tduwNetChannel)pToBeCast;
  /* we retrieved a channel number, now mark the channel as readable */
  gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsReadable=1;
  /*  and retrieve the actual message from this channel*/
  if(eL0PCWin95UDPRemoveMessage(uwChannel, ppData)==NetLib_E_es_FIFOIsEmpty)
   return NetLib_E_es_ShouldNotReach; /* paranoid: there should be at least one message in the channel */
  
  /* tell the caller which channel this message comes from */
  *p_uwChannel=uwChannel;
 
  return NetLib_E_es_NoError; /* no error */
 }
 
 /* the channel is not a broadcast channel */
 
 /* is there already a message in the incoming queue ? */
 
 if(eL0PCWin95UDPRemoveMessage(*p_uwChannel, ppData)==NetLib_E_es_NoError)
  return NetLib_E_es_NoError; /* no error */
 
 /* no : try to get a message from the socket */
 
 do 
 {
  /* read the first message available from the socket */
  uwChannel=uwL0PCWin95UDPGetMessage();
  /* until we get a message for the requested channel */
 } while(uwChannel != *p_uwChannel && uwChannel != C_uwNetInvalidChannel);
 
/* if(uwChannel == C_uwNetInvalidChannel)*/
/* return NetLib_E_es_FIFOIsEmpty;    /* error: no message available   */
 
/* if(eL0PCWin95UDPRemoveMessage(uwChannel, ppData)==NetLib_E_es_FIFOIsEmpty)*/
 return eL0PCWin95UDPRemoveMessage(*p_uwChannel, ppData);
/* return NetLib_E_es_ShouldNotReach; /* paranoid: there should be at least one message in the channel   */
 
/* return NetLib_E_es_NoError; /* no error   */
}
 
 
/*****************************************************************************
 * 
 * Description: eL0PCWin95UDPSendData
 *
 *
 *****************************************************************************
 *
 * Input:   uwChannel, channel to send the message into.
 *          pData, pointer on the block to send
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 16, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPSendData(tduwNetChannel uwChannel, tdpPointer pData)
{
 unsigned long ulBytestoSend;
 unsigned long ulBytesSent;
 
 /* verify the validity of the Channel */
 
 if(uwChannel>=C_ucL0MaxNumberOfUDPChannels)
  return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
 if(!gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsSlotInUse)
  return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
 if(!gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsReadable)
  /* is it worth a new error code ? */
  return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
 ulBytestoSend=((tdstNetMessage *) pData)->uwMessageSizeInBytes + sizeof(tdstNetMessage);
 
#if defined(NET_USE_DEBUG)
    vDebugSISISI(Net_C_Debug_UDP,"Send Type",((tdstNetMessage *) pData)->eMessageType,
                 "Size",((tdstNetMessage *) pData)->uwMessageSizeInBytes,
                 "Dest",((tdstNetMessage *) pData)->uxRecipientId);
#endif /* NET_USE_DEBUG */
 if
  (
   (ulBytesSent=gs_p_stUDPWinSocDesc->m_pfn_i_sendto
    (
     gs_iSD, pData, ulBytestoSend, 0,
     (struct sockaddr *)&gs_a_stL0PCWin95UDPChannels[uwChannel].stRemoteAddr,
     sizeof gs_a_stL0PCWin95UDPChannels[uwChannel].stRemoteAddr
    )
   )==SOCKET_ERROR
  )
 {
  ulBytesSent=gs_p_stUDPWinSocDesc->m_pfn_i_WSAGetLastError();
  if(ulBytesSent==WSAENETDOWN)
   return NetLib_E_es_SendSocketFailure;
  return NetLib_E_es_UnknownError;
 }
 
 if(ulBytesSent!=ulBytestoSend) 
  return NetLib_E_es_MessagePartlySent;
 
 /* if the message has been correctly sent, then the associated cell has to be freed */
 vFree(pData);
 
 return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * 
 * Description: eL0PCWin95UDPQueryChannelStatus
 *
 * returns the current status of the channel
 *
 *****************************************************************************
 *
 * Input:   uwChannel, channel to query
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 16, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95UDPQueryChannelStatus(tduwNetChannel uwChannel)
{
/* verify the validity of the Channel */
if(uwChannel>=C_ucL0MaxNumberOfUDPChannels)
 return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
if(!gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsSlotInUse)
 return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
if(!gs_a_stL0PCWin95UDPChannels[uwChannel].ubf1IsReadable)
 /* is it worth a new error code ? */
 return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
/* what else to report ???? */
 return NetLib_E_es_NoError;
}
/*****************************************************************************
 * 
 * Description: eLevel0InitWin95PCUDP
 *
 * initializes the global socket descriptor used for all channels
 *
 *****************************************************************************
 *
 * Input:   port number to use
 *
 * Output:  an error code 
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
 
NetLib_tdeErrorStatus eLevel0InitWin95PCUDP(void)
{
 char ac80HostName[80];          /* the name of the computer the application is running on */
 struct hostent *hp;                     /* the list of IP¨adresses associated to the host */
 
 /* find the host adress, set the corresponding global variable */
 if(gs_p_stUDPWinSocDesc->m_pfn_i_gethostname(ac80HostName, sizeof ac80HostName)==SOCKET_ERROR)
  return NetLib_E_es_InitialisationSocketError;
 
 if((hp=gs_p_stUDPWinSocDesc->m_pfn_pst_gethostbyname(ac80HostName))==NULL)
  return NetLib_E_es_InitialisationSocketError;
 
 gs_stMyAddress=*(struct in_addr *)(hp->h_addr_list[0]);
 
 gs_bL0PCWin95UDPInitState = gs_bL0PCWin95UDPInitState | 0x02;
  return NetLib_E_es_NoError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus vL0PCWin95UDPAddPort
Add a new port
//////////////////////////////////////////////////////////////////////////////// 
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Christophe Roguet
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95UDPAddPort(unsigned short uwPortNumber)
{
#if defined(NET_USE_DEBUG)
 vDebugSI(Net_C_Debug_UDP,"AddPort",uwPortNumber);
#endif /* NET_USE_DEBUG */
 return eL0PCWin95DirectUDPAddPort(uwPortNumber, INADDR_BROADCAST);
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95DirectUDPAddPort
Add a new port
//////////////////////////////////////////////////////////////////////////////// 
Input :
port number
IP address of machine to connect to
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Christophe Roguet
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DirectUDPAddPort(unsigned short uwPortNumber, long ConnectAddress)
{
 struct sockaddr_in my_name; /* the address to bind the socket to */
 int iOpt;                                       /* parameter used for socket option setting */
 
#if defined(NET_USE_DEBUG)
 vDebugSISI(Net_C_Debug_UDP,"DirectAddPort Num",uwPortNumber,"Adress",ConnectAddress);
#endif /* NET_USE_DEBUG */
 
 if(uwPortNumber < IPPORT_RESERVED)
  return NetLib_E_es_InvalidPortNumber;
 
 /* While we'v got only one socket, close it before opening a new one*/
 if(gs_iSD!=INVALID_SOCKET)
 {
  gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(gs_iSD);
  gs_iSD = INVALID_SOCKET;
 }
 /* open an IP datagram socket */
 gs_iSD = gs_p_stUDPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_DGRAM, 0);
 
 if(gs_iSD==INVALID_SOCKET)
  return NetLib_E_es_InitialisationSocketError;
        
 /* bind our socket to our specific port */
 my_name.sin_family = AF_INET;
 my_name.sin_port = gs_p_stUDPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
 my_name.sin_addr = gs_stMyAddress;
 
 if(gs_p_stUDPWinSocDesc->m_pfn_i_bind(gs_iSD, (struct sockaddr *)&my_name, sizeof my_name)<0)
  return NetLib_E_es_InitialisationSocketError;
 
 /* allow the socket to send broadcast messages */
 iOpt=~0;
 if(gs_p_stUDPWinSocDesc->m_pfn_i_setsockopt(gs_iSD, SOL_SOCKET, SO_BROADCAST, (char *)&iOpt, sizeof(iOpt))<0) 
  return NetLib_E_es_InitialisationSocketError;
 
 /* initialize channel 0 as broadcast channel  */
 /* the broadcast channel is always in use */
 gs_a_stL0PCWin95UDPChannels[0].ubf1IsSlotInUse = 1;
 
 /* this is a channel usable to send only broadcast messages */
 gs_a_stL0PCWin95UDPChannels[0].ubf1IsBroadcast = 1;
 gs_a_stL0PCWin95UDPChannels[0].ubf1IsReadable = 1;
 
 /* Set the first cell to null :*/
 gs_a_stL0PCWin95UDPChannels[0].p_FirstDataCell = (tdstNetDataCell *)C_pNull;
 
 /* set the destination adress */
 gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_family = AF_INET;
 gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_port = gs_p_stUDPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
 gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_addr.s_addr=gs_p_stUDPWinSocDesc->m_pfn_ul_htonl(ConnectAddress);
 
 /* the channel is working properly */
 gs_a_stL0PCWin95UDPChannels[0].eChannelStatus = E_ts_OK;
 gs_uwNumberOfPort++;
 gs_bL0PCWin95UDPInitState = gs_bL0PCWin95UDPInitState | 0x04;
 return NetLib_E_es_NoError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95UDPAddPortSym
Add a new port
//////////////////////////////////////////////////////////////////////////////// 
Input :
port number to use
remote IP address to send 'broadcast' messages to
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
 
modification log:
 
Date: June 6, 1996                      Author: Christophe ROGUET
        added the connect address parameter
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DirectUDPAddPortSym(unsigned short uwPortNumber, char *ConnectAddress)
{
 unsigned long NumAddr;
#if defined(NET_USE_DEBUG)
 vDebugSI(Net_C_Debug_UDP,"AddPortSym Num",uwPortNumber);
 vDebugSS(Net_C_Debug_UDP,"AddPortSym Adress : ",ConnectAddress);
#endif /* NET_USE_DEBUG */
 /* WinSock doesn't have inet_aton(), we have to use inet_addr() */
 if((NumAddr=gs_p_stUDPWinSocDesc->m_pfn_ul_inet_addr(ConnectAddress))!=INADDR_NONE)
  return eL0PCWin95DirectUDPAddPort(uwPortNumber, gs_p_stUDPWinSocDesc->m_pfn_ul_ntohl(NumAddr));
 return NetLib_E_es_InitialisationSocketError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95UDPCloseProtocol(void)
udp-Level 0 closing function
//////////////////////////////////////////////////////////////////////////////// 
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
none
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
void vL0PCWin95UDPCloseProtocol(void)
{
#if defined(NET_USE_DEBUG)
 vDebugS(Net_C_Debug_UDP,"Close protocol");
#endif /* NET_USE_DEBUG */
 gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(gs_iSD);
 
 eNetRestoreWinSock();
 gs_cIsWinsocDescInit=0;
 
 vLevel1RemoveProtocol(E_Net_pr_Windows95UDPProtocol);
}
 
/*****************************************************************************
 * 
 * Description: vL0PCWin95UDPOpenProtocol
 *
 * initialize the specified interface structure with the correct function
 * pointers to enable a protocol-independent data transmission process.
 *
 *****************************************************************************
 *
 * Input:   p_stProtocolInterface, pointer to the interface structure
 *
 * Output:  none
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95UDPOpenProtocol(void)
{
 tduwNetChannel uwChannelToInit;
 tdstNetProtocolInterface *p_stProtocolInterface;
 
#if defined(NET_USE_DEBUG)
  vDebugClear(Net_C_Debug_UDP);
  vDebugS(Net_C_Debug_UDP,"Netlib - Level 0 - UDP WIN95 : Log file");
#endif /* NET_USE_DEBUG */
 
 gs_bL0PCWin95UDPInitState = 0x00;
 
 /* bind to the DLL if necessary */
 
 if(!gs_cIsWinsocDescInit) {
	 if(eNetGetWinSockDesc(&gs_p_stUDPWinSocDesc)!=NetLib_E_es_NoError)
		 return ;
	 gs_cIsWinsocDescInit=~0;
 }
 
 /* initialize our socket descriptor */
 
 if(eLevel0InitWin95PCUDP()!=NetLib_E_es_NoError)
  return;
 
 gs_uwNumberOfPort = 0;
 
 p_stProtocolInterface=pstLevel1AddProtocol();
 
 /* logical identification of the protocol */
 p_stProtocolInterface->eProtocol = E_Net_pr_Windows95UDPProtocol;
 
 /* setup the function pointers */
 p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95UDPStartChannelScan;
 p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95UDPStartBroadcastChannelScan;
 p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95UDPNextChannel;
 p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95UDPNextBroadcastChannel;
 p_stProtocolInterface->fn_eReadData= eL0PCWin95UDPReadData;
 p_stProtocolInterface->fn_eSendData= eL0PCWin95UDPSendData;
 p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95UDPQueryChannelStatus;
 p_stProtocolInterface->fn_vLevel0NetEngine = (void (*)(void))C_pNull;
 p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95UDPCloseChannel;
 p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95UDPCloseProtocol;
 p_stProtocolInterface->eIsInternet = 1;
 
 
 /* initialize all other slots as unused channels */
 
 for (uwChannelToInit = 0; uwChannelToInit < C_ucL0MaxNumberOfUDPChannels; uwChannelToInit ++)
 {
  /* the channel is not used yet  */
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].ubf1IsSlotInUse = 0;
 
  /* this is a channel usable to send private messages */
 
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].ubf1IsBroadcast = 0;
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].ubf1IsReadable = 0;
 
  /* initialize the destination address: */
 
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].stRemoteAddr.sin_family=AF_INET;
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].stRemoteAddr.sin_port=0;
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].stRemoteAddr.sin_addr.s_addr=gs_p_stUDPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE);
 
  /* the channel is working properly */
  gs_a_stL0PCWin95UDPChannels[uwChannelToInit].eChannelStatus = E_ts_OK;
 }
 gs_iSD = INVALID_SOCKET;
 
 gs_bL0PCWin95UDPInitState = gs_bL0PCWin95UDPInitState | 0x01;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95UDPIsProtocolSet
Check if the protocol is avaible for use or not
//////////////////////////////////////////////////////////////////////////////// 
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCUDPInterface has not
been called yet or it has failed, the application should call the level 2
eInitializeGlobalData function.
NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCUDP function has not been
called yet or it has failed, the application should call the level 2 
eInitializeGlobalData function
NetLib_E_es_NoPortSelected : the eL0PCWin95UDPAddPort has not been called yet. The 
protocol has been correctly initialised, it can be used, but it won't give any
results since no port is avaible.  The application should call it.
////////////////////////////////////////////////////////////////////////////////
Creation date : June 4,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95UDPIsProtocolSet(void)
{
 if(!(gs_bL0PCWin95UDPInitState&0x01))
  return NetLib_E_es_ProtocolNotInitialized;
 if(!(gs_bL0PCWin95UDPInitState&0x02))
  return NetLib_E_es_InitialisationSocketError;
 if(!(gs_bL0PCWin95UDPInitState&0x04))
  return NetLib_E_es_NoPortSelected;
 return NetLib_E_es_True;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95UDPClosePort
Close the socket bound with the specified port number
//////////////////////////////////////////////////////////////////////////////// 
Input :
The port number to close
////////////////////////////////////////////////////////////////////////////////
Output :
None
////////////////////////////////////////////////////////////////////////////////
Creation date : June 25,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
 
void _NET_CALLING_CONV_ vL0PCWin95UDPClosePort(unsigned short uwOldPort)
{
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_UDP,"Close port",uwOldPort);
#endif /* NET_USE_DEBUG */
 if
  (
  (gs_bL0PCWin95UDPInitState&0x04)&&
  (uwOldPort == gs_p_stUDPWinSocDesc->m_pfn_uw_ntohs(gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_port))
  )
 {
  gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(gs_iSD);
  gs_iSD = INVALID_SOCKET;
  gs_a_stL0PCWin95UDPChannels[0].ubf1IsSlotInUse = 0;
  /* this is a channel usable to send only broadcast messages */
  gs_a_stL0PCWin95UDPChannels[0].ubf1IsBroadcast = 0;
  gs_a_stL0PCWin95UDPChannels[0].ubf1IsReadable = 0;
 
  /* the channel is working properly */
  gs_a_stL0PCWin95UDPChannels[0].eChannelStatus = E_ts_Invalid;
  gs_uwNumberOfPort--;
 
  gs_bL0PCWin95UDPInitState = gs_bL0PCWin95UDPInitState& ~0x04;
 }
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95UDPIsPortAvailable
Check if the port is avaible for use or not
//////////////////////////////////////////////////////////////////////////////// 
Input :
A port number to test
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_True if the port is avaible
NetLib_E_es_PortAlreayUsed if the port is already in use
An error code otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : June 25,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95UDPIsPortAvailable(unsigned short uwPortNumber)
{
 struct sockaddr_in my_name; /* the address to bind the socket to */
 int iOpt; /* parameter used for socket option setting */
 int iTempSock;
 NetLib_tdeErrorStatus eErrorReturned;
 
 /* bind to the DLL if necessary */
 if(!gs_cIsWinsocDescInit) {
	 if((eErrorReturned=eNetGetWinSockDesc(&gs_p_stUDPWinSocDesc))!=NetLib_E_es_NoError)
		 return NetLib_E_es_InitialisationSocketError;
	 gs_cIsWinsocDescInit=~0;
 }
 
 if(uwPortNumber<IPPORT_RESERVED)
  return NetLib_E_es_InvalidPortNumber;
 
 iTempSock = gs_p_stUDPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_DGRAM, 0);
 
 if(iTempSock==INVALID_SOCKET)
  return NetLib_E_es_InitialisationSocketError;
        
 /* bind our socket to our specific port */
 my_name.sin_family = AF_INET;
 my_name.sin_port = gs_p_stUDPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
 my_name.sin_addr = gs_stMyAddress;
 
 if(gs_p_stUDPWinSocDesc->m_pfn_i_bind(iTempSock, (struct sockaddr *)&my_name, sizeof my_name)<0)
 {
  if(gs_p_stUDPWinSocDesc->m_pfn_i_WSAGetLastError()==WSAEADDRINUSE)
   eErrorReturned = NetLib_E_es_PortAlreadyUsed;
  else
   eErrorReturned = NetLib_E_es_False;
  gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(iTempSock);
  return eErrorReturned;
 }
 
 /* allow the socket to send broadcast messages */
 iOpt=~0;
 if(gs_p_stUDPWinSocDesc->m_pfn_i_setsockopt(iTempSock, SOL_SOCKET, SO_BROADCAST, (char *)&iOpt, sizeof(iOpt))<0) 
 {
  gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(iTempSock);
  return NetLib_E_es_False;
 }
 gs_p_stUDPWinSocDesc->m_pfn_i_closesocket(iTempSock);
 return NetLib_E_es_True;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95UDPIsProtocolAvailable
Check if the protocol is avaible for use or not
//////////////////////////////////////////////////////////////////////////////// 
Input :
A port number to test
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_True if the port is avaible
An error code otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : June 25,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95UDPIsProtocolAvailable(void)
{
 unsigned short uwPortNumberTest;
 NetLib_tdeErrorStatus eErrorReturned;
 
 /* bind to the DLL if necessary */
 if(!gs_cIsWinsocDescInit) {
	 if((eErrorReturned=eNetGetWinSockDesc(&gs_p_stUDPWinSocDesc))!=NetLib_E_es_NoError)
		 return NetLib_E_es_InitialisationSocketError;
	 gs_cIsWinsocDescInit=~0;
 }
 
 uwPortNumberTest =  IPPORT_RESERVED+1000;
 while
  (
  ((eErrorReturned = eL0PCWin95UDPIsPortAvailable(uwPortNumberTest))==NetLib_E_es_PortAlreadyUsed)&&
  (uwPortNumberTest<IPPORT_RESERVED+1100)
  )
  uwPortNumberTest++;
 return eErrorReturned;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95UDPIsPortOpened
Returns wether the port is opened or not
//////////////////////////////////////////////////////////////////////////////// 
Input :
A port
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 27,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95UDPIsPortOpened(unsigned short uwPortNumber)
{
 if(!(gs_bL0PCWin95UDPInitState&0x04))
  return NetLib_E_es_NoPortSelected;
 if(!(uwPortNumber == gs_p_stUDPWinSocDesc->m_pfn_uw_ntohs(gs_a_stL0PCWin95UDPChannels[0].stRemoteAddr.sin_port)))
  return NetLib_E_es_False;
 return NetLib_E_es_True;
 
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95UDPMyAddress
Check if the protocol is avaible for use or not
//////////////////////////////////////////////////////////////////////////////// 
Input :
array of characters to store an alphanumeric name into
size of the array
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCUDPInterface has not
been called yet or it has failed, the application should call the level 2
eInitializeGlobalData function.
NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCUDP function has not been
called yet or it has failed, the application should call the level 2 
eInitializeGlobalData function
////////////////////////////////////////////////////////////////////////////////
Creation date : July 4,96
Author : Christophe Roguet
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus eL0PCWin95UDPGetMyAddress(char *pcReturnStringAddress, unsigned short Size)
{
char LocalStringAddress[80];
 
if(!(gs_bL0PCWin95UDPInitState&0x01))
 return NetLib_E_es_ProtocolNotInitialized;
if(!(gs_bL0PCWin95UDPInitState&0x02))
 return NetLib_E_es_InitialisationSocketError ;
 
/* translate the numerical address into an alphabetic string */
strncpy(LocalStringAddress, gs_p_stUDPWinSocDesc->m_pfn_pc_inet_ntoa(gs_stMyAddress), 80);
 
/* if the address string is too long to fit in the caller's array, return an error value */
if(strlen(LocalStringAddress)+1>Size)
{
 memset(pcReturnStringAddress, '\0', Size);
 return NetLib_E_es_UnknownError;
}
 
strcpy(pcReturnStringAddress, LocalStringAddress);
return NetLib_E_es_NoError;
}
