CnC_Remastered_Collection/REDALERT/WSPIPX.CPP

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);
}