447 lines
17 KiB
C++
447 lines
17 KiB
C++
|
//
|
||
|
// Copyright 2020 Electronic Arts Inc.
|
||
|
//
|
||
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
|
||
|
// software: you can redistribute it and/or modify it under the terms of
|
||
|
// the GNU General Public License as published by the Free Software Foundation,
|
||
|
// either version 3 of the License, or (at your option) any later version.
|
||
|
|
||
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
|
||
|
// in the hope that it will be useful, but with permitted additional restrictions
|
||
|
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
|
||
|
// distributed with this program. You should have received a copy of the
|
||
|
// GNU General Public License along with permitted additional restrictions
|
||
|
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Command & Conquer *
|
||
|
* *
|
||
|
* $Archive:: /Sun/WSPIPX.cpp $*
|
||
|
* *
|
||
|
* $Author:: Joe_b $*
|
||
|
* *
|
||
|
* $Modtime:: 8/20/97 10:54a $*
|
||
|
* *
|
||
|
* $Revision:: 6 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* *
|
||
|
* IPXInterfaceClass::IPXInterfaceClass -- Class constructor *
|
||
|
* IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card *
|
||
|
* IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing *
|
||
|
* IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "function.h"
|
||
|
#include "wspipx.h"
|
||
|
#include "ipxaddr.h"
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* IPXInterfaceClass::IPXInterfaceClass -- Class constructor *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 8/4/97 11:41AM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
IPXInterfaceClass::IPXInterfaceClass (void) : WinsockInterfaceClass()
|
||
|
{
|
||
|
/*
|
||
|
** Set the net and node addressed to their default values.
|
||
|
*/
|
||
|
memset ( BroadcastNet, 0xff, sizeof (BroadcastNet) );
|
||
|
memset ( BroadcastNode, 0xff, sizeof (BroadcastNode) );
|
||
|
memset ( MyNode, 0xff, sizeof (MyNode) );
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: card number to retrieve ID for *
|
||
|
* ptr to addr to return ID in *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 8/1/97 3:04PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
bool IPXInterfaceClass::Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr)
|
||
|
{
|
||
|
int cbOpt;
|
||
|
int cbAddr = sizeof( SOCKADDR_IPX );
|
||
|
SOCKET s;
|
||
|
SOCKADDR_IPX Addr;
|
||
|
IPX_ADDRESS_DATA IpxData;
|
||
|
|
||
|
/*
|
||
|
** Create a temporary IPX socket.
|
||
|
*/
|
||
|
s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
|
||
|
if ( s == SOCKET_ERROR ) {
|
||
|
assert ( s != SOCKET_ERROR );
|
||
|
return (false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Socket must be bound prior to calling IPX_MAX_ADAPTER_NUM
|
||
|
*/
|
||
|
memset( &Addr, 0, sizeof( Addr ));
|
||
|
Addr.sa_family = AF_IPX;
|
||
|
int err = bind( s, (SOCKADDR*) &Addr, cbAddr);
|
||
|
if ( err == SOCKET_ERROR ) {
|
||
|
assert ( err != SOCKET_ERROR );
|
||
|
closesocket (s);
|
||
|
return (false);
|
||
|
}
|
||
|
|
||
|
memset( &IpxData, 0, sizeof(IpxData));
|
||
|
|
||
|
/*
|
||
|
** Specify which adapter to check.
|
||
|
*/
|
||
|
IpxData.adapternum = card_number;
|
||
|
cbOpt = sizeof( IpxData );
|
||
|
|
||
|
/*
|
||
|
** Get information for the current adapter.
|
||
|
*/
|
||
|
err = getsockopt( s, NSPROTO_IPX, IPX_ADDRESS, (char*) &IpxData, &cbOpt );
|
||
|
if ( err == SOCKET_ERROR ) {
|
||
|
assert ( err != SOCKET_ERROR );
|
||
|
closesocket (s);
|
||
|
return (false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** IpxData contains the address for the current adapter.
|
||
|
** The network number will be needed later for broadcasts as the net number ff,ff,ff,ff
|
||
|
** doesn't work under NT.
|
||
|
**
|
||
|
** Note: Due to a bug in Win95s implementation of Winsock, only the netnum & nodenum
|
||
|
** values are correctly returned. NT returns all expected values. ST - 7/31/97 0:57AM
|
||
|
*/
|
||
|
memcpy (addr->sa_netnum, IpxData.netnum, sizeof (addr->sa_netnum));
|
||
|
memcpy (BroadcastNet, IpxData.netnum, sizeof (addr->sa_netnum));
|
||
|
memcpy (addr->sa_nodenum, IpxData.nodenum, sizeof (addr->sa_nodenum));
|
||
|
|
||
|
closesocket (s);
|
||
|
return (true);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: SOCKET number to open. This is usually VIRGIN_SOCKET *
|
||
|
* *
|
||
|
* OUTPUT: true if socket was opened without error *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 8/4/97 5:54PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
bool IPXInterfaceClass::Open_Socket( SOCKET socketnum )
|
||
|
{
|
||
|
SOCKADDR_IPX addr;
|
||
|
bool delay = true;
|
||
|
int err;
|
||
|
|
||
|
/*
|
||
|
** If Winsock is not initialised then do it now.
|
||
|
*/
|
||
|
if ( !WinsockInitialised ) {
|
||
|
if ( !Init()) return ( false );;
|
||
|
}
|
||
|
|
||
|
IPXSocketNumber = socketnum;
|
||
|
|
||
|
/*
|
||
|
** Set up the addr structure for the IPX socket
|
||
|
*/
|
||
|
addr.sa_family = AF_IPX;
|
||
|
memset (addr.sa_netnum, 0, sizeof (addr.sa_netnum));
|
||
|
memset (addr.sa_nodenum, -1, sizeof (addr.sa_nodenum));
|
||
|
addr.sa_socket = htons ( socketnum );
|
||
|
|
||
|
/*
|
||
|
** Create the socket.
|
||
|
*/
|
||
|
Socket = socket (AF_NS, SOCK_DGRAM, NSPROTO_IPX);
|
||
|
if (Socket == INVALID_SOCKET) {
|
||
|
char out[128];
|
||
|
sprintf (out, "TS: Failed to create IPX socket - error code %d.\n", GetLastError() );
|
||
|
OutputDebugString (out);
|
||
|
assert ( Socket != INVALID_SOCKET );
|
||
|
closesocket(Socket);
|
||
|
return ( false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Get the network card address. This is needed so we can bind the socket to the net card.
|
||
|
*/
|
||
|
if ( !Get_Network_Card_Address (0, &addr)){
|
||
|
closesocket ( Socket );
|
||
|
return ( false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Bind the IPX socket to the network card.
|
||
|
*/
|
||
|
if (bind ( Socket, (const struct sockaddr *) &addr, 16) == SOCKET_ERROR ){
|
||
|
char out[128];
|
||
|
sprintf (out, "TS: IPX socket bind failed with error code %d.\n", GetLastError() );
|
||
|
OutputDebugString (out);
|
||
|
assert ( false );
|
||
|
closesocket(Socket);
|
||
|
return ( false );;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Set the various options for this IPX socket
|
||
|
*/
|
||
|
unsigned long optval = true;
|
||
|
int packet_type = 4;
|
||
|
|
||
|
/*
|
||
|
** The SO_BROADCAST option allows broadcasting on this socket. This shouldn't be needed
|
||
|
** except for the bug in the Win95 implementation of Winsock which causes broadcasts to
|
||
|
** fail if it isn't set.
|
||
|
*/
|
||
|
if ( setsockopt ( Socket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval) ) == SOCKET_ERROR ) {
|
||
|
char out[128];
|
||
|
sprintf (out, "TS: Failed to set IPX socket option SO_BROADCAST - error code %d.\n", GetLastError() );
|
||
|
OutputDebugString (out);
|
||
|
assert ( false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Set the value in the packet type field for outgoing packets.
|
||
|
*/
|
||
|
err = setsockopt ( Socket, NSPROTO_IPX, IPX_PTYPE, (char*)&packet_type, sizeof(packet_type));
|
||
|
if ( err == INVALID_SOCKET ) {
|
||
|
char out[128];
|
||
|
sprintf (out, "TS: Failed to set IPX protocol option IPX_PTYPE - error code %d.\n", GetLastError() );
|
||
|
OutputDebugString (out);
|
||
|
assert ( err != INVALID_SOCKET );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Ignore all incoming packets not of this type.
|
||
|
*/
|
||
|
err = setsockopt ( Socket, NSPROTO_IPX, IPX_FILTERPTYPE, (char*)&packet_type, sizeof(packet_type));
|
||
|
if ( err == INVALID_SOCKET ) {
|
||
|
char out[128];
|
||
|
sprintf (out, "TS: Failed to set IPX protocol option IPX_FILTERTYPE - error code %d.\n", GetLastError() );
|
||
|
OutputDebugString (out);
|
||
|
assert ( err != INVALID_SOCKET );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Set the the base class socket options for buffer sizes.
|
||
|
*/
|
||
|
WinsockInterfaceClass::Set_Socket_Options();
|
||
|
|
||
|
/*
|
||
|
** Woohoo!
|
||
|
*/
|
||
|
return ( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Usual windoze message handler stuff *
|
||
|
* *
|
||
|
* OUTPUT: 0 if message was handled *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 8/4/97 5:55PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
long IPXInterfaceClass::Message_Handler(HWND , UINT message, UINT , LONG lParam)
|
||
|
{
|
||
|
|
||
|
int addr_len; // Length of address structure
|
||
|
int rc; // Result code
|
||
|
SOCKADDR_IPX addr; // Winsock IPX addressing structure
|
||
|
WinsockBufferType *packet; // Ptr to packet
|
||
|
NetNumType netnum;
|
||
|
NetNodeType nodenum;
|
||
|
|
||
|
|
||
|
/*
|
||
|
** We only handle IPX events.
|
||
|
*/
|
||
|
if ( message != WM_IPXASYNCEVENT ) return ( 1 );
|
||
|
|
||
|
|
||
|
switch ( WSAGETSELECTEVENT(lParam) ) {
|
||
|
|
||
|
/*
|
||
|
** Read event. Winsock has data it would like to give us.
|
||
|
*/
|
||
|
case FD_READ:
|
||
|
/*
|
||
|
** Clear any outstanding errors on the socket.
|
||
|
*/
|
||
|
rc = WSAGETSELECTERROR(lParam);
|
||
|
if (rc != 0) {
|
||
|
Clear_Socket_Error (Socket);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Call the Winsock recvfrom function to get the outstanding packet.
|
||
|
*/
|
||
|
addr_len = sizeof(addr);
|
||
|
rc = recvfrom ( Socket, (char*) ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len );
|
||
|
if (rc == SOCKET_ERROR) {
|
||
|
if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||
|
Clear_Socket_Error (Socket);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** rc is the number of bytes received from Winsock
|
||
|
*/
|
||
|
if ( rc ) {
|
||
|
|
||
|
/*
|
||
|
** Make a copy of the address that this packet came from.
|
||
|
*/
|
||
|
memcpy ( netnum, addr.sa_netnum, sizeof (netnum) );
|
||
|
memcpy ( nodenum, addr.sa_nodenum, sizeof (nodenum) );
|
||
|
|
||
|
/*
|
||
|
** If this packet was from me then ignore it.
|
||
|
*/
|
||
|
if ( !memcmp (netnum, BroadcastNet, sizeof (BroadcastNet)) && !memcmp(nodenum, MyNode, sizeof (MyNode)) ) {
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Create a new buffer and store this packet in it.
|
||
|
*/
|
||
|
packet = new WinsockBufferType;
|
||
|
packet->BufferLen = rc;
|
||
|
memcpy ( packet->Buffer, ReceiveBuffer, rc );
|
||
|
IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]);
|
||
|
paddress->Set_Address ( netnum, nodenum );
|
||
|
InBuffers.Add ( packet );
|
||
|
}
|
||
|
return(0);
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Write event. We send ourselves this event when we have more data to send. This
|
||
|
** event will also occur automatically when a packet has finished being sent.
|
||
|
*/
|
||
|
case FD_WRITE:
|
||
|
/*
|
||
|
** Clear any outstanding erros on the socket.
|
||
|
*/
|
||
|
rc = WSAGETSELECTERROR(lParam);
|
||
|
if (rc != 0) {
|
||
|
Clear_Socket_Error ( Socket );
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** If there are no packets waiting to be sent then bail.
|
||
|
*/
|
||
|
while ( OutBuffers.Count() != 0 ) {
|
||
|
int packetnum = 0;
|
||
|
|
||
|
/*
|
||
|
** Get a pointer to the packet.
|
||
|
*/
|
||
|
packet = OutBuffers [ packetnum ];
|
||
|
|
||
|
/*
|
||
|
** Set up the address structure of the outgoing packet
|
||
|
*/
|
||
|
addr.sa_family = AF_IPX;
|
||
|
addr.sa_socket = htons ( IPXSocketNumber );
|
||
|
|
||
|
/*
|
||
|
** Set up the address as either a broadcast address or the given address
|
||
|
*/
|
||
|
if ( packet->IsBroadcast ) {
|
||
|
memcpy ( addr.sa_netnum, BroadcastNet, sizeof (BroadcastNet) );
|
||
|
memcpy ( addr.sa_nodenum, BroadcastNode, sizeof (BroadcastNode) );
|
||
|
}else{
|
||
|
IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]);
|
||
|
paddress->Get_Address ( netnum, nodenum );
|
||
|
memcpy ( addr.sa_netnum, netnum, sizeof (netnum) );
|
||
|
memcpy ( addr.sa_nodenum, nodenum, sizeof (nodenum) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Send it.
|
||
|
** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet
|
||
|
** at this time. In this case, we clear the socket error and just exit. Winsock will
|
||
|
** send us another WRITE message when it is ready to receive more data.
|
||
|
*/
|
||
|
rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) );
|
||
|
|
||
|
if (rc == SOCKET_ERROR){
|
||
|
if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||
|
Clear_Socket_Error (Socket);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Delete the sent packet.
|
||
|
*/
|
||
|
OutBuffers.Delete ( packetnum );
|
||
|
delete packet;
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
|