/* * universal multiplayer library level 0 implementation file * transport layer: TCP PC-windows 95 */ /* * Author: Christophe Roguet after Benoit GERMAIN * * Modification Date: 07/05/96 first draft * * 29/10/96 changed the connection process, removed the UDP socket */ #include "warnings.h" #include "NetSock.h" /* * we need this one to fill the interface structure * with pointers to the correct functions */ #include "Privl0TCP.h" #include "PrivNetDef.h" #include "L0GlDef.h" #include "NetMemCo.h" #include "NetEnd.h" #include /* size of socket send buffer */ #define C_iSendBufferSize 666 /* array of descriptors to access a physical channel. */ static tdstL0PCWin95TCPChannel gs_a_stL0PCWin95TCPChannels[C_ucL0MaxNumberOfTCPChannels]; /* 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 to listen on our TCP port */ static SOCKET gs_uiListenSD; static char gs_bL0PCWin95TCPInitState; static tdstWinSockFunc *gs_p_stTCPWinSocDesc; static char gs_cIsWinsocDescInit = 0; #define DLL_FD_ISSET(fd, set) ((gs_p_stTCPWinSocDesc->m_pfn_i_WSAFDIsSet)((SOCKET)(fd), (fd_set *)(set))) /* static functions */ static tduwNetChannel uwL0PCWin95TCPOpenChannel(SOCKET); static void vL0PCWin95TCPCloseChannel(tduwNetChannel); static NetLib_tdeErrorStatus eL0PCWin95TCPConfigureSocket(SOCKET); /*****************************************************************************/ /***************************************************************************** * * Description: uwL0PCWin95TCPGetChannel * * 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 uwL0PCWin95TCPGetChannel(struct sockaddr_in *Key) { tduwNetChannel uwCurrentChannel; for(uwCurrentChannel=0; uwCurrentChannelsin_addr.s_addr ) return uwCurrentChannel; return C_uwNetInvalidChannel; /* Error invalid channel ?*/ } /***************************************************************************** * * Description: uwL0PCWin95TCPOpenChannel * * returns the identification number of an unused slot in the channel table * ***************************************************************************** * * Input: inet address to bind to the channel * * Output: identification of the channel * ***************************************************************************** * Creation Date: April 24, 1996 Author: Christophe ROGUET ***************************************************************************** * * Modification log: * * Date: Author: * ****************************************************************************/ static tduwNetChannel uwL0PCWin95TCPOpenChannel(SOCKET uiSD) { tduwNetChannel uwCurrentChannel; /* scan the channel table until we find an unused slot */ for( uwCurrentChannel=0; uwCurrentChannel=C_ucL0MaxNumberOfTCPChannels) return /*NetLib_E_es_InvalidChannel*/; /* the channel does not exist in the table */ pstChannel=&gs_a_stL0PCWin95TCPChannels[uwChannel]; if(!pstChannel->ubf1IsSlotInUse) return /*NetLib_E_es_ChannelUninitialized*/; /* the slot is not initialized for use */ /* clear the use mark */ pstChannel->ubf1IsSlotInUse=0; /* release the socket descriptor for this channel */ gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(pstChannel->uiSD); /* set the remote address to none */ pstChannel->stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE); } /***************************************************************************** * * Description: uwL0PCWin95TCPStartChannelScan * * 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 uwL0PCWin95TCPStartChannelScan(void) { tduwNetChannel uwCurrentChannel; /* scan the array of channel definitions */ uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1; while ( (uwCurrentChannel!=C_uwNetInvalidChannel)&& (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse) ) uwCurrentChannel--; /* return its index value */ return uwCurrentChannel; } /***************************************************************************** * * Description: uwL0PCWin95TCPNextChannel * * 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 uwL0PCWin95TCPNextChannel(tduwNetChannel uwLastScannedChannel) { tduwNetChannel uwCurrentChannel; /* * scan the array of channel definitions, starting with the channel following * the last channel returned */ uwCurrentChannel = (unsigned short)(uwLastScannedChannel-1); while ( (uwCurrentChannel!=C_uwNetInvalidChannel)&& (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse) ) uwCurrentChannel--; /* else no slot is in use */ return uwCurrentChannel; } /***************************************************************************** * * Description: uwL0PCWin95TCPStartBroadcastChannelScan * * 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 uwL0PCWin95TCPStartBroadcastChannelScan(void) { tduwNetChannel uwCurrentChannel; /* scan the array of channel definitions */ uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1; while ( (uwCurrentChannel!=C_uwNetInvalidChannel)&& ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)|| (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast)) ) uwCurrentChannel--; /* else no slot is in use */ return uwCurrentChannel; } /***************************************************************************** * * Description: uwL0PCWin95TCPNextBroadcastChannel * * 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 uwL0PCWin95TCPNextBroadcastChannel(tduwNetChannel uwLastScannedChannel) { tduwNetChannel uwCurrentChannel; uwCurrentChannel = (unsigned short)(uwLastScannedChannel-1); while ( (uwCurrentChannel!=C_uwNetInvalidChannel)&& ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)|| (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast)) ) uwCurrentChannel--; /* else no slot is in use */ return uwCurrentChannel; } /***************************************************************************** * * Description: uwL0PCWin95TCPGetNewReconnectChannel * * returns the identification number of a reconnect channel whose connection succeeded * returns C_uwNetInvalidChannel if there is no such channel ***************************************************************************** * * Input: none * * Output: identification of the channel * ***************************************************************************** * Creation Date: January 24, 1997 Author: Christophe ROGUET ***************************************************************************** * * Modification log: * * Date: Author: * ****************************************************************************/ tduwNetChannel uwL0PCWin95TCPGetNewReconnectChannel(void) { tduwNetChannel uwCurrentChannel; /* scan the array of channel definitions */ uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1; while ( (uwCurrentChannel!=C_uwNetInvalidChannel)&& ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)|| (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsReconnect) || (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsConnected)) ) uwCurrentChannel--; if(uwCurrentChannel!=C_uwNetInvalidChannel) /* if we found a new channel, unmark it */ gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsReconnect=0; return uwCurrentChannel; } /***************************************************************************** * * Description: eL0PCWin95TCPReadData * * ***************************************************************************** * * 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 * 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: 19/06/96 Author: Christophe ROGUET * stopped using ioctlsocket(FIONREAD), which doesn't work as expected and was * causing a bug when using the library over internet * instead, I now use recv(MSG_PEEK) and test the return value * Date: 25/10/96 Author: Christophe ROGUET * test the recv() return value for 0 : detects graceful disconnections * Date: 29/10/96 Author: Christophe ROGUET * no UDP socket/broadcast channel any more ****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TCPReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData) { tdstL0PCWin95TCPChannel *pstChannel; /* Channel to read from */ tdstNetMessage stMsgHeader; /* header of the incoming message */ unsigned long ulMsgSize; /* length of the incoming message (header included) */ int iErrorCode; long lRecvdBytes; /* verify the validity of the Channel */ if(*p_uwChannel>=C_ucL0MaxNumberOfTCPChannels) return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */ pstChannel=&gs_a_stL0PCWin95TCPChannels[*p_uwChannel]; if(!pstChannel->ubf1IsSlotInUse) return NetLib_E_es_ChannelUninitialized; /* the slot is not initialized for use */ if(!pstChannel->ubf1IsConnected) /* is it worth a new error code ? */ return NetLib_E_es_ChannelUninitialized; /* the slot should not be accessed yet */ /* try to get a message from the socket */ /* if no message is being received, try to read a header from the socket and allocate a buffer for the message */ if(pstChannel->pMsg==C_pNull) { if((lRecvdBytes=gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD,(char *)&stMsgHeader, sizeof stMsgHeader, MSG_PEEK)) ==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); if(iErrorCode==WSAEWOULDBLOCK) return NetLib_E_es_FIFOIsEmpty; /* error: no message available */ if(iErrorCode==WSAECONNRESET) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"connection reset",pstChannel->uiSD); #endif /* NET_USE_DEBUG */ /* the connection has been broken */ vL0PCWin95TCPCloseChannel(*p_uwChannel); return NetLib_E_es_ChannelIsInvalid; } return NetLib_E_es_UnknownError; } if(lRecvdBytes==0) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"graceful disconnection",pstChannel->uiSD); #endif /* NET_USE_DEBUG */ /* the connection has been closed by the remote end */ vL0PCWin95TCPCloseChannel(*p_uwChannel); return NetLib_E_es_ChannelIsInvalid; } if(lRecvdBytes!=sizeof stMsgHeader) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"recv MSG_PEEK partiel",pstChannel->uiSD); #endif /* NET_USE_DEBUG */ return NetLib_E_es_FIFOIsEmpty; /* error: header partially available */ } /* if big/little endian differs, swap the header before anything else*/ if (stMsgHeader.uxHeadBigEndian!=NetLib_ucGetLittleBigEndian()) NetLib_eSwapLittleBigEndianMsgHeader(&stMsgHeader); if ( (M_uwCheckSumSlot((&stMsgHeader))!=M_uwProcessCheckSum((&stMsgHeader))) || (stMsgHeader.eMessageType >= E_Net_mt_LastSysMsg)|| (stMsgHeader.eMessageType <= E_Net_mt_InvalidMessage) ) { #if defined(NET_USE_DEBUG) vDebugSDump(Net_C_Debug_TCP,"type de message incorrect",&stMsgHeader, sizeof stMsgHeader); #endif /* NET_USE_DEBUG */ return NetLib_E_es_UnknownError; } #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"Read data",pstChannel->uiSD); vDebugSI(Net_C_Debug_TCP,"\t\tType de message",stMsgHeader.eMessageType); vDebugSI(Net_C_Debug_TCP,"\t\tTaille du message",stMsgHeader.uwMessageSizeInBytes); vDebugSI(Net_C_Debug_TCP,"\t\tExpediteur",stMsgHeader.uxSenderId); vDebugSI(Net_C_Debug_TCP,"\t\tDestinataire",stMsgHeader.uxRecipientId); #endif /* NET_USE_DEBUG */ /* compute the message length */ ulMsgSize=stMsgHeader.uwMessageSizeInBytes + sizeof(tdstNetMessage); pstChannel->pMsg=pMalloc(ulMsgSize); if(pstChannel->pMsg == C_pNull) { /* the pointer couldn't be allocated :*/ /* Purge the message : DOES THIS WORK FINE WITH A STREAM SOCKET ?????? */ if(gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD, NULL, ulMsgSize, 0)<0) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"Read data Error allocation impossible",gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError()); #endif /* NET_USE_DEBUG */ return NetLib_E_es_UnknownError; } /* Return en invalid channel :*/ return NetLib_E_es_NotEnoughMemory; } /* no bytes of this message have been read yet */ pstChannel->ulBytesRead=0; pstChannel->ulBytesLeft=ulMsgSize; } else { #if defined(NET_USE_DEBUG) vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead, "bytes left",pstChannel->ulBytesLeft); #endif /* NET_USE_DEBUG */ } if((lRecvdBytes=gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD, &pstChannel->pMsg[pstChannel->ulBytesRead], pstChannel->ulBytesLeft, 0))<0) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"Read data Error",gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError()); #endif /* NET_USE_DEBUG */ iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); if(iErrorCode==WSAEWOULDBLOCK) return NetLib_E_es_FIFOIsEmpty; /* free the message buffer or not ??? */ return NetLib_E_es_UnknownError; } #if defined(NET_USE_DEBUG) if(pstChannel->ulBytesLeft!=0) { vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead, "bytes left",pstChannel->ulBytesLeft); } #endif /* NET_USE_DEBUG */ pstChannel->ulBytesRead+=lRecvdBytes; pstChannel->ulBytesLeft-=lRecvdBytes; if(pstChannel->ulBytesLeft>0) { /* has the message been read in whole ??? */ #if defined(NET_USE_DEBUG) vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead, "bytes left",pstChannel->ulBytesLeft); #endif /* NET_USE_DEBUG */ return NetLib_E_es_FIFOIsEmpty; /* error: no full message available yet */ } /* return the message */ *ppData=pstChannel->pMsg; pstChannel->pMsg=C_pNull; return NetLib_E_es_NoError; /* no error */ } /***************************************************************************** * * Description: eL0PCWin95TCPSendData * * ***************************************************************************** * * 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: October 29, 1996 Author: Christophe ROGUET * no more UDP socket/broadcast channel *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TCPSendData(tduwNetChannel uwChannel, tdpPointer pData) { unsigned long ulBytestoSend; unsigned long ulBytesSent; int iErrorCode; #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"Send data",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD); vDebugSI(Net_C_Debug_TCP,"\t\tType de message",((tdstNetMessage*)pData)->eMessageType); vDebugSI(Net_C_Debug_TCP,"\t\tTaille du message",((tdstNetMessage*)pData)->uwMessageSizeInBytes); vDebugSI(Net_C_Debug_TCP,"\t\tExpediteur",((tdstNetMessage*)pData)->uxSenderId); vDebugSI(Net_C_Debug_TCP,"\t\tDestinataire",((tdstNetMessage*)pData)->uxRecipientId); #endif /* NET_USE_DEBUG */ /* verify the validity of the Channel */ if(uwChannel>=C_ucL0MaxNumberOfTCPChannels) return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */ if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse) return NetLib_E_es_ChannelUninitialized; /* the slot is not initialized for use */ if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected) /* 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); /* connected channel: write the data to the connected socket */ if((ulBytesSent=gs_p_stTCPWinSocDesc->m_pfn_i_send(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, pData, ulBytestoSend, 0))==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); if(iErrorCode==WSAECONNRESET) { /* the connection has been closed by the remote side */ #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"Send data :connection reset"); #endif vL0PCWin95TCPCloseChannel(uwChannel); return NetLib_E_es_ChannelIsInvalid; } if(iErrorCode==WSAEWOULDBLOCK) { /* the system is blocking */ #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"Send data :would block"); #endif return NetLib_E_es_EmissionInProgress; } #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"Send data :socket failure, error",iErrorCode); #endif return NetLib_E_es_SendSocketFailure; } if(ulBytesSent!=ulBytestoSend) { #if defined(NET_USE_DEBUG) vDebugSISI(Net_C_Debug_TCP,"Send data :partly sent BytesSent",ulBytesSent,"BytestoSend", ulBytestoSend); #endif return NetLib_E_es_MessagePartlySent; } /* if the routine succeeded in sending the message, then it's its duty to free the associated cell */ vFree(pData); return NetLib_E_es_NoError; } /***************************************************************************** * * Description: eL0PCWin95TCPQueryChannelStatus * * 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: October 29, 1996 Author: Christophe ROGUET * the IsReadable flag does not exist any more ****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TCPQueryChannelStatus(tduwNetChannel uwChannel) { /* verify the validity of the Channel */ int iErrorCode; int iParameterSize; if(uwChannel>=C_ucL0MaxNumberOfTCPChannels) return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */ if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse) return NetLib_E_es_ChannelUninitialized; /* the slot is not initialized for use */ if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected) /* is it worth a new error code ? */ return NetLib_E_es_ChannelUninitialized; /* the slot should not be accessed yet */ /* what else to report ???? */ iParameterSize=sizeof iErrorCode; if ( gs_p_stTCPWinSocDesc->m_pfn_i_getsockopt ( gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,SOL_SOCKET, SO_ERROR,(char *)&iErrorCode, &iParameterSize )==SOCKET_ERROR ) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_UnknownError; } #if defined(NET_USE_DEBUG) if(iErrorCode!=0) { vDebugSISISI(Net_C_Debug_TCP,"QueryChannelStatus(): on channel",uwChannel, "socket",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,"error",iErrorCode); } #endif switch(iErrorCode) { case 0: return NetLib_E_es_NoError; case WSAECONNRESET: /* the connection has been closed by the remote end */ vL0PCWin95TCPCloseChannel(uwChannel); return NetLib_E_es_ChannelIsInvalid; default: return NetLib_E_es_UnknownError; } } void vL0PCWin95TCPGSConnectEngine(); /* //////////////////////////////////////////////////////////////////////////////// Description : void eL0PCWin95TCPNetEngine(void) The level 0 NetEngine function for TCP connections //////////////////////////////////////////////////////////////////////////////// Input : None //////////////////////////////////////////////////////////////////////////////// Output : NetLib_E_es_NoError An NetLib_tdeErrorStatus if an error occured //////////////////////////////////////////////////////////////////////////////// Creation date : May 7, 96 Author : Christophe Roguet modification log: Date: October 25, 1996 Author: Christophe ROGUET modified the exception detection //////////////////////////////////////////////////////////////////////////////// */ void vL0PCWin95TCPNetEngine(void) { static fd_set stReadFD, stWriteFD, stExceptionFD; static struct timeval stTimeOut = { 0, 0 }; tduwNetChannel uwChannel; /* channel number attributed to new connections */ int iErrorCode; SOCKET uiSD; int iAddrLength; /* getpeername() needs a variable argument */ int iOptLen; /* is there any connection operation waiting to be serviced ? */ /* build an fd-set to test: */ FD_ZERO(&stReadFD); FD_ZERO(&stWriteFD); FD_ZERO(&stExceptionFD); /* ability to read on our listening socket */ FD_SET(gs_uiListenSD, &stReadFD); /* ability to write on sockets for which a connect() call has been issued and presence of an exception for the same sockets and the sockets already used */ for(uwChannel=0; uwChannelm_pfn_i_select(0, &stReadFD, &stWriteFD, &stExceptionFD, &stTimeOut)==SOCKET_ERROR) { gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return; /* an error code ??? */ } /* service connection requests: */ /* accept() on our listening socket until there are no more pending requests */ if(DLL_FD_ISSET(gs_uiListenSD, &stReadFD)) do { uiSD=gs_p_stTCPWinSocDesc->m_pfn_ui_accept(gs_uiListenSD,NULL,NULL); if(uiSD==INVALID_SOCKET) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); /* no more pending connections ? */ if(iErrorCode==WSAEWOULDBLOCK) break; gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiSD); return; /* an error code ??? */ } #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"accept()",uiSD); #endif /* got a new connection from a remote player: allocate a new channel */ uwChannel=uwL0PCWin95TCPOpenChannel(uiSD); if(uwChannel==C_uwNetInvalidChannel) { gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiSD); return; /* return an error code ? */ } /* set the channel's connection flag */ gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected=1; gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast=1; /* set the channel's remote address */ iAddrLength=sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr); if(gs_p_stTCPWinSocDesc->m_pfn_i_getpeername(uiSD, (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr, &iAddrLength)==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); /* if an error occured, simply close the channel .... */ vL0PCWin95TCPCloseChannel(uwChannel); } } while(1); /* service connection completion: */ /* mark the channels that are ready to be used */ /* re- attempt to connect the channels whose connection has failed */ for(uwChannel=0; uwChannelm_pfn_i_getsockopt(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, SOL_SOCKET, SO_ERROR, (char *)&iErrorCode, &iOptLen) == SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); continue; } #if defined(NET_USE_DEBUG) vDebugSISISI(Net_C_Debug_TCP,"exception on channel",uwChannel, "socket",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,"error",iErrorCode); #endif /* release the corresponding channel */ /* vL0PCWin95TCPCloseChannel(uwChannel); */ /* attempt a new connection */ /* connect the socket to the remote host */ if(gs_p_stTCPWinSocDesc->m_pfn_i_connect(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr, sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr))==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); if(iErrorCode!=WSAEWOULDBLOCK) { vL0PCWin95TCPCloseChannel(uwChannel); } } } } } vL0PCWin95TCPGSConnectEngine(); } /***************************************************************************** * * Description: eL0PCWin95TCPInit * * loads the sockets dll * ***************************************************************************** * * Input: * * Output: an error code * ***************************************************************************** * Creation Date: April 24, 1996 Author: Christophe ROGUET ***************************************************************************** * * Modification log: * * Date: Author: * ****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TCPInit(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 */ if(!(gs_bL0PCWin95TCPInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; /* find the host address, set the corresponding global variable */ if(gs_p_stTCPWinSocDesc->m_pfn_i_gethostname(ac80HostName, sizeof ac80HostName)==SOCKET_ERROR) return NetLib_E_es_InitialisationSocketError; if((hp=gs_p_stTCPWinSocDesc->m_pfn_pst_gethostbyname(ac80HostName))==NULL) return NetLib_E_es_InitialisationSocketError; gs_stMyAddress=*(struct in_addr *)(hp->h_addr_list[0]); gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x02; return NetLib_E_es_NoError; } /* //////////////////////////////////////////////////////////////////////////////// Description : tdeErrorStatus eL0PCWin95TCPAddPort Add a new port //////////////////////////////////////////////////////////////////////////////// Input : port number to use //////////////////////////////////////////////////////////////////////////////// Output : a tdeErrorStatus //////////////////////////////////////////////////////////////////////////////// Creation date : May 6,96 Author : Albert Pais //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPAddPort(unsigned short uwPortNumber) { return eL0PCWin95DirectTCPAddPort(uwPortNumber, INADDR_NONE); } /* //////////////////////////////////////////////////////////////////////////////// Description : NetLib_tdeErrorStatus eL0PCWin95TCPAddPortSym 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_ eL0PCWin95DirectTCPAddPortSym(unsigned short uwPortNumber, char *ConnectAddress) { unsigned long NumAddr; /* WinSock doesn't have inet_aton(), we have to use inet_addr() */ if((NumAddr=gs_p_stTCPWinSocDesc->m_pfn_ul_inet_addr(ConnectAddress))!=INADDR_NONE) return eL0PCWin95DirectTCPAddPort(uwPortNumber, gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(NumAddr)); return NetLib_E_es_InitialisationSocketError; } /* //////////////////////////////////////////////////////////////////////////////// Description : NetLib_tdeErrorStatus eL0PCWin95TCPReconnect try to connect to an address to have a direct route to a player //////////////////////////////////////////////////////////////////////////////// Input : port number to use remote IP address to send 'broadcast' messages to //////////////////////////////////////////////////////////////////////////////// Output : a NetLib_tdeErrorStatus //////////////////////////////////////////////////////////////////////////////// Creation date : January 24, 1997 Author : Christophe Roguet modification log: //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPReconnect(unsigned short uwPortNumber, long lConnectAddress) { tduwNetChannel uwCurrentChannel; /* if we already have a channel for this target address, return */ for(uwCurrentChannel=0; uwCurrentChannelm_pfn_ul_ntohl(lConnectAddress)) return NetLib_E_es_NoError; /* initiate an asynchronous connection to the target address */ return eL0PCWin95TCPChannelConnect(uwPortNumber, lConnectAddress, eReconnect, (tduwNetChannel *)C_pNull); } /* //////////////////////////////////////////////////////////////////////////////// Description : NetLib_tdeErrorStatus eL0PCWin95DirectTCPAddPort Add a new port //////////////////////////////////////////////////////////////////////////////// Input : port number to use IP address of remote host to connect to //////////////////////////////////////////////////////////////////////////////// Output : a NetLib_tdeErrorStatus //////////////////////////////////////////////////////////////////////////////// Creation date :June 6, 1996 Author: Christophe ROGUET Modification log: Date: Author: October 29, 1996 Christophe Roguet no more UDP socket at channel 0; attempts to connect to the eventual remote host January 24, 1997 Christophe Roguet connect part moved to a new function: eL0PCWin95TCPChannelConnect February 14, 1997 Christophe Roguet close the socket descriptor when an error occurs //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DirectTCPAddPort(unsigned short uwPortNumber, long lConnectAddress) { struct sockaddr_in my_name; /* the address to bind the socket to */ NetLib_tdeErrorStatus eErrorCode; if(uwPortNumberm_pfn_ui_socket(PF_INET, SOCK_STREAM, 0); if(gs_uiListenSD==INVALID_SOCKET) return NetLib_E_es_InitialisationSocketError; if(eL0PCWin95TCPConfigureSocket(gs_uiListenSD)!=NetLib_E_es_NoError) eErrorCode = NetLib_E_es_InitialisationSocketError; else { /* bind this socket to our specific port */ my_name.sin_family = AF_INET; my_name.sin_port = gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber); my_name.sin_addr = gs_stMyAddress; if(gs_p_stTCPWinSocDesc->m_pfn_i_bind(gs_uiListenSD, (struct sockaddr *)&my_name, sizeof my_name)<0) eErrorCode = NetLib_E_es_InitialisationSocketError; else { /* start listening on this socket */ if(gs_p_stTCPWinSocDesc->m_pfn_i_listen(gs_uiListenSD, 5)==SOCKET_ERROR) /* 5 is the maximum backlog supported by Windobe */ eErrorCode = NetLib_E_es_InitialisationSocketError; else { eErrorCode=NetLib_E_es_NoError; /* if the remote address is a valid address, initiate handshake with the remote end */ if(lConnectAddress!=INADDR_NONE) { eErrorCode=eL0PCWin95TCPChannelConnect(uwPortNumber, lConnectAddress, eFirstConnect, (tduwNetChannel *)C_pNull); } } } } if(eErrorCode!=NetLib_E_es_NoError) { gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD); gs_uiListenSD=INVALID_SOCKET; } else gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x04; return eErrorCode; } /* //////////////////////////////////////////////////////////////////////////////// Description : NetLib_tdeErrorStatus eL0PCWin95TCPChannelConnect open a new channel and initiate an asynchronous connection to a target address //////////////////////////////////////////////////////////////////////////////// Input : port number to use IP address of remote host to connect to connection mode : either eFirstConnect or eReconnect puwChannel : (output parameter) pointer to a variable that can hold the chosen channel number; can be C_pNull //////////////////////////////////////////////////////////////////////////////// Output : a NetLib_tdeErrorStatus //////////////////////////////////////////////////////////////////////////////// Creation date :January 24, 1997 Author: Christophe ROGUET (code moved from TCPDIrectConnect) Modification log: //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPChannelConnect(unsigned short uwPortNumber, long ConnectAddress, int iMode, tduwNetChannel *puwChannel) { int iErrorCode; tduwNetChannel uwChannel; /* channel number for the connection attempt */ SOCKET uiNewSD; /* socket descriptor for the connection attempt */ /* open a new socket */ uiNewSD=gs_p_stTCPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_STREAM, 0); if(uiNewSD==INVALID_SOCKET) return NetLib_E_es_InitialisationSocketError; /* set options on the socket */ if(eL0PCWin95TCPConfigureSocket(uiNewSD)!=NetLib_E_es_NoError) { gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiNewSD); return NetLib_E_es_InitialisationSocketError; } /* allocate a new channel */ if((uwChannel=uwL0PCWin95TCPOpenChannel(uiNewSD))==C_uwNetInvalidChannel) { gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiNewSD); return NetLib_E_es_InitialisationSocketError; } /* if the caller wants to know the new channel number, tell him */ if(puwChannel!=(tduwNetChannel *)C_pNull) *puwChannel=uwChannel; /* mark the channel as 'broadcast' */ gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast=1; /* store the target address in the channel */ gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_family = AF_INET; gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_port=gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber); gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(ConnectAddress); #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"connect()"); #endif /* connect the socket to the remote host */ if(gs_p_stTCPWinSocDesc->m_pfn_i_connect(uiNewSD, (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr, sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr))==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); if(iErrorCode!=WSAEWOULDBLOCK) { vL0PCWin95TCPCloseChannel(uwChannel); return NetLib_E_es_InitialisationSocketError; } } /* differenciate between first connection (which will eventually result in a new broadcast channel) */ /* and re-connection (which will eventually result in a new 'reconnection' channel) */ switch(iMode) { case eFirstConnect: /* this is a channel usable to send only broadcast messages */ gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast = 1; gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsReconnect = 0; break; case eReconnect: gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast = 0; gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsReconnect = 1; break; } /* the channel is working properly */ gs_a_stL0PCWin95TCPChannels[uwChannel].eChannelStatus = E_ts_OK; return NetLib_E_es_NoError; } /* //////////////////////////////////////////////////////////////////////////////// Description : vL0PCWin95TCPCloseProtocol(void) TCP-Level 0 closing function //////////////////////////////////////////////////////////////////////////////// Input : none //////////////////////////////////////////////////////////////////////////////// Output : none //////////////////////////////////////////////////////////////////////////////// Creation date : May 6,96 Author : Albert Pais //////////////////////////////////////////////////////////////////////////////// */ void vL0PCWin95TCPCloseProtocol(void) { tduwNetChannel uwChannel; /* close the listening socket */ gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD); /* close all channel sockets still open */ for(uwChannel=0; uwChannelm_pfn_i_closesocket(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD); eNetRestoreWinSock(); gs_cIsWinsocDescInit=0; vLevel1RemoveProtocol(E_Net_pr_Windows95TCPProtocol); #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"\n\nBye-bye\n"); #endif } /* //////////////////////////////////////////////////////////////////////////////// Description : eL0PCWin95TCPIsProtocolSet Check if the protocol is avaible for use or not //////////////////////////////////////////////////////////////////////////////// Input : none //////////////////////////////////////////////////////////////////////////////// Output : NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTCPInterface has not been called yet or it has failed, the application should call the level 2 eInitializeGlobalData function. NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTCP function has not been called yet or it has failed, the application should call the level 2 eInitializeGlobalData function NetLib_E_es_NoPortSelected : the eL0PCWin95TCPAddPort 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_ eL0PCWin95TCPIsProtocolSet(void) { if(!(gs_bL0PCWin95TCPInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; if(!(gs_bL0PCWin95TCPInitState&0x02)) return NetLib_E_es_InitialisationSocketError; if(!(gs_bL0PCWin95TCPInitState&0x04)) return NetLib_E_es_NoPortSelected; return NetLib_E_es_NoError; } /* //////////////////////////////////////////////////////////////////////////////// Description : eL0PCWin95TCPIsPortAvailable 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 26,96 Author : Albert Pais Modification log: Date: October 29, 1996 Author: Christophe ROGUET no more UDP socket Date: October 29, 1996 Author: Christophe ROGUET close the socket descriptor even if an error occurs before binding //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsPortAvailable(unsigned short uwPortNumber) { struct sockaddr_in my_name; /* the address to bind the socket to */ SOCKET uiTempSock; NetLib_tdeErrorStatus tdeErrorCode; /* bind to the DLL if necessary */ if(!gs_cIsWinsocDescInit) { if((tdeErrorCode=eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc))!=NetLib_E_es_NoError) return NetLib_E_es_InitialisationSocketError; gs_cIsWinsocDescInit=~0; } if(uwPortNumberm_pfn_ui_socket(PF_INET, SOCK_STREAM, 0); if(uiTempSock==INVALID_SOCKET) return NetLib_E_es_InitialisationSocketError; if(eL0PCWin95TCPConfigureSocket(uiTempSock)!=NetLib_E_es_NoError) tdeErrorCode = NetLib_E_es_InitialisationSocketError; else { my_name.sin_family = AF_INET; my_name.sin_port = gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber); my_name.sin_addr = gs_stMyAddress; if(gs_p_stTCPWinSocDesc->m_pfn_i_bind(uiTempSock, (struct sockaddr *)&my_name, sizeof(my_name))<0) tdeErrorCode = NetLib_E_es_InitialisationSocketError; tdeErrorCode = NetLib_E_es_True; } gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiTempSock); return tdeErrorCode; } /* //////////////////////////////////////////////////////////////////////////////// Description : eL0PCWin95TCPIsProtocolAvailable Check if the protocol is avaible for use or not //////////////////////////////////////////////////////////////////////////////// Input : None //////////////////////////////////////////////////////////////////////////////// Output : NetLib_E_es_True if the port is avaible An error code otherwise //////////////////////////////////////////////////////////////////////////////// Creation date : June 26,96 Author : Albert Pais //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsProtocolAvailable(void) { unsigned short uwPortNumberTest; NetLib_tdeErrorStatus eErrorReturned; /* bind to the DLL if necessary */ if(!gs_cIsWinsocDescInit) { if((eErrorReturned=eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc))!=NetLib_E_es_NoError) return NetLib_E_es_InitialisationSocketError; gs_cIsWinsocDescInit=~0; } uwPortNumberTest = IPPORT_RESERVED+1000; while ( ((eErrorReturned = eL0PCWin95TCPIsPortAvailable(uwPortNumberTest))==NetLib_E_es_PortAlreadyUsed)&& (uwPortNumberTestm_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return; } if(!(uwOldPort == gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port))) return; /* close the socket used for listening */ gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD); gs_uiListenSD = INVALID_SOCKET; /* close all used channels */ for(uwCurrentChannel=0; uwCurrentChannelm_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_UnknownError; } if(!(uwPortNumber == gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port))) return NetLib_E_es_False; return NetLib_E_es_True; } /* //////////////////////////////////////////////////////////////////////////////// Description : eL0PCWin95TCPGetMyAddress copy into a character string the numerical IP address which we are bound to //////////////////////////////////////////////////////////////////////////////// Input : array of characters to store an alphanumeric name into size of the array //////////////////////////////////////////////////////////////////////////////// Output : NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTCPInterface has not been called yet or it has failed, the application should call the level 2 eInitializeGlobalData function. NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTCP 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 _NET_CALLING_CONV_ eL0PCWin95TCPGetMyAddress(char *pcReturnStringAddress, unsigned short Size) { char LocalStringAddress[80]; if(!(gs_bL0PCWin95TCPInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; if(!(gs_bL0PCWin95TCPInitState&0x02)) eL0PCWin95TCPInit(); if(!(gs_bL0PCWin95TCPInitState&0x02)) return NetLib_E_es_InitialisationSocketError ; /* translate the numerical address into an alphabetic string */ strncpy(LocalStringAddress, gs_p_stTCPWinSocDesc->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; } NetLib_tdeErrorStatus eL0PCWin95TCPGetPortAndAddress(unsigned short *p_uwPort, unsigned long *p_ulAddress) { struct sockaddr_in ListenSockName; /* the address that the listen socket is bound to */ int iAddrLen; int iErrorCode; if(!(gs_bL0PCWin95TCPInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; if(!(gs_bL0PCWin95TCPInitState&0x02)) eL0PCWin95TCPInit(); if(!(gs_bL0PCWin95TCPInitState&0x02)) return NetLib_E_es_InitialisationSocketError ; /* copy the requested information into the return parameters */ /* get the port number from the listen socket */ if(gs_uiListenSD==INVALID_SOCKET) { *p_uwPort=0; /* if we are not bound, return 0 as port number */ } else { iAddrLen=sizeof(ListenSockName); if(gs_p_stTCPWinSocDesc->m_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_UnknownError; } *p_uwPort=gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port); } /* get the IP address from the global variable */ *p_ulAddress=gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(gs_stMyAddress.s_addr); return NetLib_E_es_NoError; } /* //////////////////////////////////////////////////////////////////////////////// Description : eL0PCWin95TCPConfigureSocket set appropriate options on a socket //////////////////////////////////////////////////////////////////////////////// Input : uiSD the socket descriptor of the socket to configure //////////////////////////////////////////////////////////////////////////////// Output : NetLib_E_es_InitialisationSocketError if a system call returned an error NetLib_E_es_NoError if the socket could be properly configured //////////////////////////////////////////////////////////////////////////////// Creation date : October 29,96 Author : Christophe Roguet //////////////////////////////////////////////////////////////////////////////// */ NetLib_tdeErrorStatus eL0PCWin95TCPConfigureSocket(SOCKET uiSD) { int iOpt; /* parameter used for socket option setting via setsockopt() */ int iOptLen; /* length of option retrieved with getsockopt() */ unsigned long ulOpt; /* parameter used for socket option setting via ioctlsocket() */ int iErrorCode; /* set non blocking mode on this socket */ ulOpt=(unsigned long)~0L; if(gs_p_stTCPWinSocDesc->m_pfn_i_ioctlsocket(uiSD, FIONBIO, &ulOpt)==SOCKET_ERROR) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_InitialisationSocketError; } /* disable the Nagle algorithm on this socket */ /* (in order not to be blocked because a small message won't go out) */ iOpt=~0; if(gs_p_stTCPWinSocDesc->m_pfn_i_setsockopt(uiSD, IPPROTO_TCP, TCP_NODELAY, (char *)&iOpt, sizeof(iOpt))<0) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_InitialisationSocketError; } /* set the system buffer size for send operations */ /* if it is too big, it will cause a lag because lots of messages will be stored in this buffer */ iOpt=C_iSendBufferSize; iOptLen=sizeof(iOpt); if(gs_p_stTCPWinSocDesc->m_pfn_i_setsockopt(uiSD, SOL_SOCKET, SO_SNDBUF, (char *)&iOpt, iOptLen)<0) { iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError(); return NetLib_E_es_InitialisationSocketError; } return NetLib_E_es_NoError; } /*****************************************************************************/ /* functions related to gameservice direct reconnection */ /*****************************************************************************/ /* 5 s to perform all connections */ #define GS_CONNECTION_TIMEOUT 5000 typedef struct _stTargetCell { unsigned short uwPortNumber; /* port number to reconnect to */ unsigned long ulTargetIPAddr; /* IP address to reconnect to */ tduwNetChannel uwChannel; /* channel associated to this target */ char cRouteKnown; /* flag: level 1 knows this route */ struct _stTargetCell *pstNextTarget; /* link to next cell in list */ } tdstTargetCell; unsigned short gs_pstL0PCWin95TCP_GS_ListenPort=0; /* socket number to listen on */ tdstTargetCell *gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull; /* list of target addresses to reconnect to */ unsigned char gs_ucL0PCWin95TCPConnecting=0; /* flag: are we currently in a connection process ? */ unsigned long gs_ulL0PCWin95TCPConnectionStartTime; /* time at which the connect process began */ static tdfn_vConnectionResult gs_pfnConnectionResult; /* Call back for connection result. */ /*****************************************************************************/ /* start listening on a port */ void _NET_CALLING_CONV_ vL0PCWin95TCPGSSetCallBackResult(tdfn_vConnectionResult pfnConnectionResult) { gs_pfnConnectionResult=pfnConnectionResult; } /*****************************************************************************/ /* start listening on a port */ void GS_FUNCTIONCALL vL0PCWin95TCPGSSetPLayer(unsigned short uwPlayerId, unsigned short uwPortNumber) { /* the playerId argument is ignored ... */ #if defined(NET_USE_DEBUG) vDebugSISI(Net_C_Debug_TCP,"SetPlayer PlayerId",uwPlayerId,"Port",uwPortNumber); #endif eL0PCWin95TCPAddPort(uwPortNumber); gs_pstL0PCWin95TCP_GS_ListenPort=uwPortNumber; } /*****************************************************************************/ /* add a new entry to the list of targets to reconnect to */ void GS_FUNCTIONCALL vL0PCWin95TCPGSSetChannel(unsigned short uwPortNumber, char *pcIPAddr) { tdstTargetCell *pstTargetCell; /* new list element for the new target */ tdstTargetCell *pstCurrentCell; /* pointer to run through the list */ #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"SetChannel Port",uwPortNumber); vDebugSS(Net_C_Debug_TCP,"Addr : ",pcIPAddr); #endif /* do not begin to register a new list before the former has not been processed */ if(gs_ucL0PCWin95TCPConnecting) return; /* allocate a new cell */ pstTargetCell=(tdstTargetCell *)pMalloc(sizeof(tdstTargetCell)); if(pstTargetCell==(tdstTargetCell *)C_pNull) return; /* not enough memory */ /* fill the new cell */ pstTargetCell->uwPortNumber=uwPortNumber; pstTargetCell->ulTargetIPAddr= gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(gs_p_stTCPWinSocDesc->m_pfn_ul_inet_addr(pcIPAddr)); pstTargetCell->uwChannel=C_uwNetInvalidChannel; pstTargetCell->cRouteKnown=0; /* insert the new cell at the tail of the list */ if(gs_pstL0PCWin95TCPTargetsList==(tdstTargetCell *)C_pNull) { pstTargetCell->pstNextTarget=(tdstTargetCell *)C_pNull; gs_pstL0PCWin95TCPTargetsList=pstTargetCell; } else { pstTargetCell->pstNextTarget=(tdstTargetCell *)C_pNull; pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell->pstNextTarget!=(tdstTargetCell *)C_pNull) pstCurrentCell=pstCurrentCell->pstNextTarget; pstCurrentCell->pstNextTarget=pstTargetCell; } } /*****************************************************************************/ /* start reconnecting to the targets */ void GS_FUNCTIONCALL vL0PCWin95TCPGSInitiateConnection(void) { tdstTargetCell *pstCurrentCell; /* pointer to run through the list */ #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"InitiateConnection"); #endif /* record the start time */ gs_ulL0PCWin95TCPConnectionStartTime=GetTickCount(); /* for each entry in the list, allocate a channel and launch an asynchronous connection */ pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell!=(tdstTargetCell *)C_pNull) { eL0PCWin95TCPChannelConnect(pstCurrentCell->uwPortNumber, pstCurrentCell->ulTargetIPAddr, eReconnect, &pstCurrentCell->uwChannel); pstCurrentCell=pstCurrentCell->pstNextTarget; } /* set the connection flag */ gs_ucL0PCWin95TCPConnecting=(unsigned char)~0; } /*****************************************************************************/ void _NET_CALLING_CONV_ vL0PCWin95TCPCloseListenPort() { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"Close listen port"); #endif vL0PCWin95TCPClosePort(gs_pstL0PCWin95TCP_GS_ListenPort); gs_pstL0PCWin95TCP_GS_ListenPort=0; } /*****************************************************************************/ void vL0PCWin95TCPNewRouteKnown(tduwNetChannel uwChannel) { tdstTargetCell *pstCurrentCell; /* pointers to run through the list */ #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_TCP,"route through channel known",uwChannel); #endif pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell!=(tdstTargetCell *)C_pNull) { if(pstCurrentCell->uwChannel==uwChannel) { pstCurrentCell->cRouteKnown=1; break; } pstCurrentCell=pstCurrentCell->pstNextTarget; } } /*****************************************************************************/ void vL0PCWin95TCPGSConnectEngine() { char cCompleted; tdstTargetCell *pstCurrentCell, *pstNextCell; /* pointers to run through the list */ if(gs_ucL0PCWin95TCPConnecting) { /* check for connection process timeout/completion */ cCompleted=1; pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell!=(tdstTargetCell *)C_pNull) { if(!gs_a_stL0PCWin95TCPChannels[pstCurrentCell->uwChannel].ubf1IsConnected) { cCompleted=0; break; } if(!pstCurrentCell->cRouteKnown) { cCompleted=0; break; } pstCurrentCell=pstCurrentCell->pstNextTarget; } if(cCompleted) { /* all connections succeeded */ /* free the target list */ pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell!=(tdstTargetCell *)C_pNull) { pstNextCell=pstCurrentCell->pstNextTarget; vFree(pstCurrentCell); pstCurrentCell=pstNextCell; } gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull; /* alert the gameservice */ #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"Connection result 1"); #endif if (gs_pfnConnectionResult) gs_pfnConnectionResult(1); gs_ucL0PCWin95TCPConnecting=0; } else if(GetTickCount()-gs_ulL0PCWin95TCPConnectionStartTime>GS_CONNECTION_TIMEOUT) { /* time is over */ /* close all channels and free the target list */ pstCurrentCell=gs_pstL0PCWin95TCPTargetsList; while(pstCurrentCell!=(tdstTargetCell *)C_pNull) { pstNextCell=pstCurrentCell->pstNextTarget; vL0PCWin95TCPCloseChannel(pstCurrentCell->uwChannel); vFree(pstCurrentCell); pstCurrentCell=pstNextCell; } gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull; /* close the listen port */ vL0PCWin95TCPCloseListenPort(); /* alert the gameservice */ #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_TCP,"Connection result 0"); #endif if (gs_pfnConnectionResult) gs_pfnConnectionResult(1); gs_ucL0PCWin95TCPConnecting=0; } } } /*****************************************************************************/ /***************************************************************************** * * Description: vL0PCWin95TCPOpenProtocol * * 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: * October 29, 1996 Christophe Roguet * the CloseChannel interface function is now null ****************************************************************************/ void _NET_CALLING_CONV_ vL0PCWin95TCPOpenProtocol(void) { tduwNetChannel uwChannelToInit; tdstNetProtocolInterface *p_stProtocolInterface; tdstTCPReconnectInterface *p_stReconnectInterface; p_stReconnectInterface=pstNetGetTCPReconnectInterface(); p_stReconnectInterface->pfn_eTCPReconnect=eL0PCWin95TCPReconnect; p_stReconnectInterface->pfn_eTCPGetPortAndAddress=eL0PCWin95TCPGetPortAndAddress; p_stReconnectInterface->pfn_vTCPNewRouteKnown=vL0PCWin95TCPNewRouteKnown; p_stReconnectInterface->pfn_uwTCPGetNewReconnectChannel=uwL0PCWin95TCPGetNewReconnectChannel; /* initialize our global socket descriptors */ #if defined(NET_USE_DEBUG) vDebugClear(Net_C_Debug_TCP); vDebugS(Net_C_Debug_TCP,"Netlib - Level 0 - TCP WIN95 : Log file"); #endif /* NET_USE_DEBUG */ gs_bL0PCWin95TCPInitState = 0x00; /* bind to the DLL if necessary */ if(!gs_cIsWinsocDescInit) { if(eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc)!=NetLib_E_es_NoError) return ; gs_cIsWinsocDescInit=~0; } p_stProtocolInterface=pstLevel1AddProtocol(); /* logical identification of the protocol */ p_stProtocolInterface->eProtocol = E_Net_pr_Windows95TCPProtocol; /* setup the function pointers */ p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95TCPStartChannelScan; p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95TCPStartBroadcastChannelScan; p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95TCPNextChannel; p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95TCPNextBroadcastChannel; p_stProtocolInterface->fn_eReadData= eL0PCWin95TCPReadData; p_stProtocolInterface->fn_eSendData= eL0PCWin95TCPSendData; p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95TCPQueryChannelStatus; p_stProtocolInterface->fn_vLevel0NetEngine = vL0PCWin95TCPNetEngine; p_stProtocolInterface->fn_vCloseChannel = (tdfn_vCloseChannel)C_pNull; p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95TCPCloseProtocol; p_stProtocolInterface->eIsInternet = 1; /* initialize all slots as unused channels */ for (uwChannelToInit = 0; uwChannelToInit < C_ucL0MaxNumberOfTCPChannels; uwChannelToInit ++) { /* the channel is not used yet */ gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsSlotInUse = 0; /* this is a channel usable to send private messages */ gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsBroadcast = 0; gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsConnected = 0; /* initialize the destination address: */ gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_family=AF_INET; gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_port=0; gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE); /* the channel is working properly */ gs_a_stL0PCWin95TCPChannels[uwChannelToInit].eChannelStatus = E_ts_OK; } gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x01; gs_pfnConnectionResult=NULL; }