mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
1004 lines
23 KiB
C
1004 lines
23 KiB
C
![]() |
/*
|
||
|
GameSpy Ping SDK
|
||
|
Dan "Mr. Pants" Schoenblum
|
||
|
dan@gamespy.com
|
||
|
|
||
|
Copyright 1999-2007 GameSpy Industries, Inc
|
||
|
|
||
|
devsupport@gamespy.com
|
||
|
*/
|
||
|
|
||
|
/****************
|
||
|
** DEFINITIONS **
|
||
|
*****************
|
||
|
|
||
|
UPP = UDP Ping Protocol
|
||
|
|
||
|
*/
|
||
|
|
||
|
/*************
|
||
|
** INCLUDES **
|
||
|
*************/
|
||
|
#include <limits.h>
|
||
|
#include "pinger.h"
|
||
|
#include "../darray.h"
|
||
|
|
||
|
/************
|
||
|
** DEFINES **
|
||
|
************/
|
||
|
#define PI_MAGIC 0x91
|
||
|
#define PI_VERSION 0x01
|
||
|
|
||
|
#define PI_TRIP2_TIMEOUT 10000
|
||
|
|
||
|
#define PI_DATA_MAX_LEN (PINGER_UDP_PING_SIZE - sizeof(piUDPPing))
|
||
|
|
||
|
/**********
|
||
|
** TYPES **
|
||
|
**********/
|
||
|
// u_char must be 1 byte unsigned, and unsigned short must be 2 bytes unsigned.
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
typedef unsigned char uint8;
|
||
|
typedef unsigned short uint16;
|
||
|
|
||
|
// This is the internal data for a UDP ping.
|
||
|
// This is an exact copy of what gets sent over the network, minus filler bytes.
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
typedef struct piUDPPing
|
||
|
{
|
||
|
uint8 magic; // magic number - helps to ignore bad packets
|
||
|
uint8 version; // version - UPP version
|
||
|
uint16 trip; // trip number - will always be 1, 2, or 3
|
||
|
uint16 ID_A; // this ID is used by A (the source for the first dgram)
|
||
|
uint16 ID_B; // this ID is used by B (the destination for the first dgram)
|
||
|
} piUDPPing;
|
||
|
|
||
|
// This is a ping on which we're waiting for a reply.
|
||
|
/////////////////////////////////////////////////////
|
||
|
typedef struct piActivePing
|
||
|
{
|
||
|
PINGERBool originator; // true if we sent the first dgram
|
||
|
uint16 ID; // unique ID for this ping
|
||
|
uint16 expectedTrip; // expected trip # of the reply
|
||
|
gsi_time timestamp; // time we sent the ping
|
||
|
gsi_time timeout; // time this ping expires
|
||
|
unsigned int remoteIP; // IP of the other end of this ping
|
||
|
unsigned short remotePort; // port of the other end of this ping
|
||
|
pingerGotPing reply; // call this callback if we're the originator (otherwise call pingerPinged)
|
||
|
void * replyParam; // the extra param passed to reply
|
||
|
} piActivePing;
|
||
|
|
||
|
// This is a queued gotPing callback.
|
||
|
/////////////////////////////////////
|
||
|
typedef struct piQueuedCallback
|
||
|
{
|
||
|
unsigned int IP;
|
||
|
unsigned short port;
|
||
|
int ping;
|
||
|
char * data;
|
||
|
int len;
|
||
|
void * param;
|
||
|
pingerGotPing callback;
|
||
|
} piQueuedCallback;
|
||
|
|
||
|
/************
|
||
|
** GLOBALS **
|
||
|
************/
|
||
|
static PINGERBool piInitialized = PINGERFalse;
|
||
|
static SOCKET piSocket = INVALID_SOCKET;
|
||
|
static pingerGotPing piPingerPinged;
|
||
|
static void * piPingerPingedParam;
|
||
|
static pingerSetData piPingerSetData;
|
||
|
static void * piPingerSetDataParam;
|
||
|
static PINGERBool piSettingData;
|
||
|
static PINGERBool piUDPEnabled;
|
||
|
static uint16 piNextID;
|
||
|
static DArray piActivePingList;
|
||
|
static gsi_time piLastThinkTime;
|
||
|
static DArray piCallbacks;
|
||
|
|
||
|
/**************
|
||
|
** FUNCTIONS **
|
||
|
**************/
|
||
|
static uint16 piGetNextID(void)
|
||
|
{
|
||
|
// Store the next ID.
|
||
|
/////////////////////
|
||
|
uint16 ID = piNextID;
|
||
|
|
||
|
// Increment the ID.
|
||
|
////////////////////
|
||
|
if(piNextID == USHRT_MAX)
|
||
|
piNextID = 1;
|
||
|
else
|
||
|
piNextID++;
|
||
|
|
||
|
return ID;
|
||
|
}
|
||
|
|
||
|
static PINGERBool piBytesToPing(unsigned char * buffer, piUDPPing * udpPing, char * data)
|
||
|
{
|
||
|
assert(buffer != NULL);
|
||
|
assert(udpPing != NULL);
|
||
|
assert(data != NULL);
|
||
|
|
||
|
// Magic.
|
||
|
udpPing->magic = *buffer++;
|
||
|
|
||
|
// Version.
|
||
|
udpPing->version = *buffer++;
|
||
|
|
||
|
// Trip.
|
||
|
udpPing->trip = (unsigned short)(*buffer++ << 8);
|
||
|
udpPing->trip |= *buffer++;
|
||
|
|
||
|
// ID_A.
|
||
|
udpPing->ID_A = (unsigned short)(*buffer++ << 8);
|
||
|
udpPing->ID_A |= *buffer++;
|
||
|
|
||
|
// ID_B.
|
||
|
udpPing->ID_B = (unsigned short)(*buffer++ << 8);
|
||
|
udpPing->ID_B |= *buffer++;
|
||
|
|
||
|
// Check for the magic number.
|
||
|
// This asserted on 11/17/00 at aprox. 1:40PM.
|
||
|
// on a ping from 205.251.192.203:13139.
|
||
|
// There were 2 empty bytes at the start of
|
||
|
// the buffer, then the normal message followed.
|
||
|
// Same thing on 12/14/00 at approx. 5PM.
|
||
|
// From 24.156.198.26
|
||
|
// JED 12/18/00 15:20, again from 24.156.198.26
|
||
|
// 2001.Jan.15.JED - again from 24.156.198.26
|
||
|
// 2001.Jan.23.JED - 213.65.117.53 (@11:15am)
|
||
|
// 2001.Mar.26.JED - 24.159.107.0 (@7:30pm)
|
||
|
// PANTS|04.20.00 - commented out per JED's request.
|
||
|
////////////////////////////////////////////////////
|
||
|
//assert(udpPing->magic == PI_MAGIC);
|
||
|
if(udpPing->magic != PI_MAGIC)
|
||
|
return PINGERFalse;
|
||
|
|
||
|
// Check the version.
|
||
|
/////////////////////
|
||
|
assert(udpPing->version == PI_VERSION);
|
||
|
if(udpPing->version != PI_VERSION)
|
||
|
return PINGERFalse;
|
||
|
|
||
|
// Check the trip.
|
||
|
//////////////////
|
||
|
assert((udpPing->trip >= 1) && (udpPing->trip <= 3));
|
||
|
if((udpPing->trip < 1) || (udpPing->trip > 3))
|
||
|
return PINGERFalse;
|
||
|
|
||
|
// Fill in the data.
|
||
|
////////////////////
|
||
|
memcpy(data, buffer, PI_DATA_MAX_LEN);
|
||
|
|
||
|
return PINGERTrue;
|
||
|
}
|
||
|
|
||
|
static void piPingToBytes(piUDPPing * udpPing, unsigned char * buffer)
|
||
|
{
|
||
|
assert(udpPing != NULL);
|
||
|
assert(buffer != NULL);
|
||
|
|
||
|
// Magic.
|
||
|
*buffer++ = udpPing->magic;
|
||
|
|
||
|
// Version.
|
||
|
*buffer++ = udpPing->version;
|
||
|
|
||
|
// Trip.
|
||
|
*buffer++ = (unsigned char)((udpPing->trip & 0xFF00) >> 8);
|
||
|
*buffer++ = (unsigned char)(udpPing->trip & 0x00FF);
|
||
|
|
||
|
// ID_A.
|
||
|
*buffer++ = (unsigned char)((udpPing->ID_A & 0xFF00) >> 8);
|
||
|
*buffer++ = (unsigned char)(udpPing->ID_A & 0x00FF);
|
||
|
|
||
|
// ID_B.
|
||
|
*buffer++ = (unsigned char)((udpPing->ID_B & 0xFF00) >> 8);
|
||
|
*buffer++ = (unsigned char)(udpPing->ID_B & 0x00FF);
|
||
|
}
|
||
|
|
||
|
static PINGERBool piSendPing(SOCKADDR_IN * to, uint16 trip, uint16 ID_A, uint16 ID_B, const char * data)
|
||
|
{
|
||
|
unsigned char buffer[PINGER_UDP_PING_SIZE];
|
||
|
piUDPPing udpPing;
|
||
|
int rcode;
|
||
|
|
||
|
assert(to != NULL);
|
||
|
assert((trip >= 1) && (trip <= 3));
|
||
|
assert(!((trip == 2) && (ID_A == 0)));
|
||
|
|
||
|
// Construct the outgoing ping.
|
||
|
///////////////////////////////
|
||
|
udpPing.magic = PI_MAGIC;
|
||
|
udpPing.version = PI_VERSION;
|
||
|
udpPing.trip = trip;
|
||
|
udpPing.ID_A = ID_A;
|
||
|
udpPing.ID_B = ID_B;
|
||
|
piPingToBytes(&udpPing, buffer);
|
||
|
if(data != NULL)
|
||
|
memcpy(buffer + sizeof(piUDPPing), data, PI_DATA_MAX_LEN);
|
||
|
else
|
||
|
memset(buffer + sizeof(piUDPPing), 0, PI_DATA_MAX_LEN);
|
||
|
|
||
|
// Send the outgoing ping.
|
||
|
//////////////////////////
|
||
|
rcode = sendto(piSocket, (char *)buffer, PINGER_UDP_PING_SIZE, 0, (SOCKADDR *)to, sizeof(SOCKADDR_IN));
|
||
|
// Did it send ok?
|
||
|
//////////////////
|
||
|
if(rcode != PINGER_UDP_PING_SIZE)
|
||
|
return PINGERFalse;
|
||
|
|
||
|
return PINGERTrue;
|
||
|
}
|
||
|
|
||
|
static PINGERBool piSocketInit(const char * localAddress,
|
||
|
unsigned short localPort)
|
||
|
{
|
||
|
int rcode;
|
||
|
SOCKADDR_IN sockaddr;
|
||
|
int bFlag;
|
||
|
//int iLastError;
|
||
|
|
||
|
assert(localPort != 0);
|
||
|
|
||
|
// Setup sockets.
|
||
|
/////////////////
|
||
|
SocketStartUp();
|
||
|
|
||
|
// Setup the address.
|
||
|
/////////////////////
|
||
|
memset(&sockaddr, 0, sizeof(SOCKADDR_IN));
|
||
|
sockaddr.sin_family = AF_INET;
|
||
|
sockaddr.sin_port = htons(localPort);
|
||
|
if(localAddress != NULL)
|
||
|
{
|
||
|
unsigned int IP;
|
||
|
|
||
|
// Check for "a.b.c.d".
|
||
|
///////////////////////
|
||
|
IP = inet_addr(localAddress);
|
||
|
if(IP == INADDR_NONE)
|
||
|
{
|
||
|
HOSTENT * hostent;
|
||
|
|
||
|
// Check for "machine.host.domain".
|
||
|
///////////////////////////////////
|
||
|
hostent = gethostbyname(localAddress);
|
||
|
if(hostent == NULL)
|
||
|
{
|
||
|
// iLastError = WSAGetLastError();
|
||
|
assert(0);
|
||
|
return PINGERFalse;
|
||
|
}
|
||
|
|
||
|
// Grab the IP.
|
||
|
///////////////
|
||
|
assert(IP != 0);
|
||
|
IP = *(unsigned int *)hostent->h_addr_list[0];
|
||
|
}
|
||
|
|
||
|
// Got the IP.
|
||
|
//////////////
|
||
|
sockaddr.sin_addr.s_addr = IP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Any local IP.
|
||
|
////////////////
|
||
|
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
||
|
}
|
||
|
|
||
|
// Create the socket.
|
||
|
/////////////////////
|
||
|
piSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||
|
if(piSocket == INVALID_SOCKET)
|
||
|
{
|
||
|
// iLastError = GOAGetLastError(piSocket);
|
||
|
assert(0);
|
||
|
return PINGERFalse;
|
||
|
}
|
||
|
|
||
|
// Allow reuse of the socket if not closed properly before.
|
||
|
///////////////////////////////////////////////////////////
|
||
|
bFlag = 1;
|
||
|
#ifndef INSOCK // PS2 INSOCK network layer does not support reuse addr
|
||
|
rcode = setsockopt(piSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&bFlag, sizeof(bFlag) );
|
||
|
#endif
|
||
|
/*if(gsiSocketIsError(rcode))
|
||
|
{
|
||
|
iLastError = WSAGetLastError();
|
||
|
assert(0);
|
||
|
return PINGERFalse;
|
||
|
}*/
|
||
|
|
||
|
// Bind the socket.
|
||
|
///////////////////
|
||
|
rcode = bind(piSocket, (SOCKADDR *)&sockaddr, sizeof(SOCKADDR_IN));
|
||
|
if (gsiSocketIsError(rcode))
|
||
|
{
|
||
|
// iLastError = GOAGetLastError(piSocket);
|
||
|
assert(0);
|
||
|
return PINGERFalse;
|
||
|
}
|
||
|
|
||
|
return PINGERTrue;
|
||
|
}
|
||
|
|
||
|
static void piQueueCallback
|
||
|
(
|
||
|
unsigned int IP,
|
||
|
unsigned short port,
|
||
|
int ping,
|
||
|
const char * data,
|
||
|
int len,
|
||
|
void * param,
|
||
|
pingerGotPing callbackFunc
|
||
|
)
|
||
|
{
|
||
|
piQueuedCallback callback;
|
||
|
|
||
|
assert(callbackFunc);
|
||
|
if(!callbackFunc)
|
||
|
return;
|
||
|
|
||
|
// Setup the callback.
|
||
|
//////////////////////
|
||
|
callback.IP = IP;
|
||
|
callback.port = port;
|
||
|
callback.ping = ping;
|
||
|
callback.len = len;
|
||
|
callback.param = param;
|
||
|
callback.callback = callbackFunc;
|
||
|
|
||
|
// Copy the data.
|
||
|
/////////////////
|
||
|
if(data)
|
||
|
{
|
||
|
callback.data = (char *)gsimalloc((unsigned int)len);
|
||
|
if(!callback.data)
|
||
|
return;
|
||
|
memcpy(callback.data, data, (unsigned int)len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
callback.data = NULL;
|
||
|
}
|
||
|
|
||
|
// Add it.
|
||
|
//////////
|
||
|
ArrayAppend(piCallbacks, &callback);
|
||
|
}
|
||
|
|
||
|
static void piCallCallbacks(void)
|
||
|
{
|
||
|
piQueuedCallback * callback;
|
||
|
piQueuedCallback callbackCopy;
|
||
|
|
||
|
while(ArrayLength(piCallbacks) > 0)
|
||
|
{
|
||
|
// Get the info.
|
||
|
////////////////
|
||
|
callback = (piQueuedCallback *)ArrayNth(piCallbacks, 0);
|
||
|
assert(callback);
|
||
|
if(!callback)
|
||
|
return;
|
||
|
|
||
|
// Copy the info.
|
||
|
/////////////////
|
||
|
memcpy(&callbackCopy, callback, sizeof(piQueuedCallback));
|
||
|
callback = &callbackCopy;
|
||
|
|
||
|
// Remove it from the list.
|
||
|
///////////////////////////
|
||
|
ArrayDeleteAt(piCallbacks, 0);
|
||
|
|
||
|
// Call it.
|
||
|
///////////
|
||
|
callback->callback(callback->IP, callback->port, callback->ping, callback->data, callback->len, callback->param);
|
||
|
|
||
|
// gsifree it.
|
||
|
///////////
|
||
|
gsifree(callback->data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PINGERBool pingerInit(const char * localAddress,
|
||
|
unsigned short localPort,
|
||
|
pingerGotPing pinged,
|
||
|
void * pingedParam,
|
||
|
pingerSetData setData,
|
||
|
void * setDataParam)
|
||
|
{
|
||
|
// Check if we're already initialized.
|
||
|
//////////////////////////////////////
|
||
|
assert(!piInitialized);
|
||
|
|
||
|
// Make sure the UDP ping size is at least as large as the protocol struct.
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
#if !defined(_NITRO)
|
||
|
assert(PINGER_UDP_PING_SIZE >= sizeof(piUDPPing));
|
||
|
#endif
|
||
|
|
||
|
// Check if we're already intialized.
|
||
|
/////////////////////////////////////
|
||
|
if(piInitialized)
|
||
|
return PINGERFalse;
|
||
|
|
||
|
// An empty localAddress is them same as a NULL.
|
||
|
////////////////////////////////////////////////
|
||
|
if((localAddress != NULL) && (localAddress[0] == '\0'))
|
||
|
localAddress = NULL;
|
||
|
|
||
|
// Initialize data.
|
||
|
///////////////////
|
||
|
piPingerPinged = pinged;
|
||
|
piPingerPingedParam = pingedParam;
|
||
|
piPingerSetData = setData;
|
||
|
piPingerSetDataParam = setDataParam;
|
||
|
piSettingData = PINGERFalse;
|
||
|
piUDPEnabled = (PINGERBool)(localPort != 0);
|
||
|
piNextID = 1;
|
||
|
piLastThinkTime = 0;
|
||
|
|
||
|
// Initialize the active ping list.
|
||
|
///////////////////////////////////
|
||
|
piActivePingList = ArrayNew(sizeof(piActivePing), 0, NULL);
|
||
|
if(piActivePingList == NULL)
|
||
|
return PINGERFalse;
|
||
|
|
||
|
// Initialize the callbacks list.
|
||
|
/////////////////////////////////
|
||
|
piCallbacks = ArrayNew(sizeof(piQueuedCallback), 0, NULL);
|
||
|
if(!piCallbacks)
|
||
|
{
|
||
|
ArrayFree(piActivePingList);
|
||
|
return PINGERFalse;
|
||
|
}
|
||
|
|
||
|
// Setup the UDP socket.
|
||
|
////////////////////////
|
||
|
if(piUDPEnabled)
|
||
|
{
|
||
|
if(!piSocketInit(localAddress, localPort))
|
||
|
{
|
||
|
assert(0);
|
||
|
|
||
|
// Check for partial intialization.
|
||
|
///////////////////////////////////
|
||
|
if(piSocket != INVALID_SOCKET)
|
||
|
{
|
||
|
closesocket(piSocket);
|
||
|
piSocket = INVALID_SOCKET;
|
||
|
}
|
||
|
|
||
|
// Shut down sockets.
|
||
|
/////////////////////
|
||
|
SocketShutDown();
|
||
|
|
||
|
// gsifree the lists.
|
||
|
//////////////////
|
||
|
ArrayFree(piActivePingList);
|
||
|
ArrayFree(piCallbacks);
|
||
|
|
||
|
return PINGERFalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We're initialized!
|
||
|
/////////////////////
|
||
|
piInitialized = PINGERTrue;
|
||
|
|
||
|
return PINGERTrue;
|
||
|
}
|
||
|
|
||
|
void pingerShutdown(void)
|
||
|
{
|
||
|
if(!piInitialized)
|
||
|
return;
|
||
|
|
||
|
// Check for setting data.
|
||
|
//////////////////////////
|
||
|
if(piSettingData)
|
||
|
return;
|
||
|
|
||
|
// Shut down the socket.
|
||
|
////////////////////////
|
||
|
if(piSocket != INVALID_SOCKET)
|
||
|
{
|
||
|
closesocket(piSocket);
|
||
|
piSocket = INVALID_SOCKET;
|
||
|
}
|
||
|
|
||
|
// Shut down sockets.
|
||
|
/////////////////////
|
||
|
SocketShutDown();
|
||
|
|
||
|
// gsifree the active ping list.
|
||
|
/////////////////////////////
|
||
|
ArrayFree(piActivePingList);
|
||
|
|
||
|
// gsifree the callbacks list.
|
||
|
///////////////////////////
|
||
|
ArrayFree(piCallbacks);
|
||
|
|
||
|
// Not initialized anymore.
|
||
|
///////////////////////////
|
||
|
piInitialized = PINGERFalse;
|
||
|
}
|
||
|
|
||
|
static int GS_STATIC_CALLBACK piFindActivePingCompareFn(const void *elem1, const void *elem2)
|
||
|
{
|
||
|
piActivePing * activePing1 = (piActivePing *)elem1;
|
||
|
piActivePing * activePing2 = (piActivePing *)elem2;
|
||
|
assert(activePing1 != NULL);
|
||
|
assert(activePing2 != NULL);
|
||
|
assert(activePing1->ID != 0);
|
||
|
assert(activePing2->ID != 0);
|
||
|
|
||
|
return (activePing1->ID - activePing2->ID);
|
||
|
}
|
||
|
|
||
|
static piActivePing * piFindActivePing(uint16 ID, int * index)
|
||
|
{
|
||
|
piActivePing key;
|
||
|
int n;
|
||
|
|
||
|
// Search for a matching ID.
|
||
|
////////////////////////////
|
||
|
key.ID = ID;
|
||
|
n = ArraySearch(piActivePingList, &key, piFindActivePingCompareFn, 0, 0);
|
||
|
if(index != NULL)
|
||
|
*index = n;
|
||
|
|
||
|
// Find it?
|
||
|
///////////
|
||
|
if(n == NOT_FOUND)
|
||
|
return NULL;
|
||
|
|
||
|
// Found it!
|
||
|
////////////
|
||
|
return (piActivePing *)ArrayNth(piActivePingList, n);
|
||
|
}
|
||
|
|
||
|
static int piCalculatePing(gsi_time sendTime, gsi_time recvTime)
|
||
|
{
|
||
|
int ping;
|
||
|
|
||
|
// Simple ping calculation.
|
||
|
///////////////////////////
|
||
|
ping = (int)(recvTime - sendTime);
|
||
|
|
||
|
// Take off some time based on the last time we called
|
||
|
// the think function.
|
||
|
//////////////////////////////////////////////////////
|
||
|
if(piLastThinkTime != 0)
|
||
|
{
|
||
|
#if 0
|
||
|
ping -= ((int)(recvTime - piLastThinkTime) / 2);
|
||
|
if(ping < 0)
|
||
|
ping = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return ping;
|
||
|
}
|
||
|
|
||
|
static void piProcessTrip1(piUDPPing * udpPing, const char * data, SOCKADDR_IN * from, gsi_time recvTime)
|
||
|
{
|
||
|
char dataOut[PI_DATA_MAX_LEN];
|
||
|
uint16 ID;
|
||
|
|
||
|
// Validity check.
|
||
|
// udpPing->ID_A != 0 was triggered by a ping
|
||
|
// from 213.105.94.117 sometime during 7/3-7/02
|
||
|
///////////////////////////////////////////////
|
||
|
assert(udpPing->trip == 1);
|
||
|
//assert(udpPing->ID_A != 0);
|
||
|
if(udpPing->ID_A == 0)
|
||
|
return;
|
||
|
assert(udpPing->ID_B == 0);
|
||
|
if(udpPing->ID_B != 0)
|
||
|
return;
|
||
|
|
||
|
// Do we want a reply?
|
||
|
//////////////////////
|
||
|
if(piPingerPinged != NULL)
|
||
|
{
|
||
|
// Get an ID.
|
||
|
/////////////
|
||
|
ID = piGetNextID();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No reply wanted.
|
||
|
///////////////////
|
||
|
ID = 0;
|
||
|
}
|
||
|
|
||
|
// Setup the data.
|
||
|
//////////////////
|
||
|
memset(dataOut, 0, PI_DATA_MAX_LEN);
|
||
|
if(piPingerSetData != NULL)
|
||
|
{
|
||
|
piSettingData = PINGERTrue;
|
||
|
piPingerSetData(from->sin_addr.s_addr, from->sin_port, dataOut, PI_DATA_MAX_LEN, piPingerSetDataParam);
|
||
|
piSettingData = PINGERFalse;
|
||
|
}
|
||
|
|
||
|
// Send the return ping (trip 2).
|
||
|
/////////////////////////////////
|
||
|
piSendPing(from, 2, udpPing->ID_A, ID, dataOut);
|
||
|
|
||
|
// Do we need to add an active ping?
|
||
|
////////////////////////////////////
|
||
|
if(piPingerPinged != NULL)
|
||
|
{
|
||
|
piActivePing activePing;
|
||
|
|
||
|
// Setup the active ping object.
|
||
|
////////////////////////////////
|
||
|
activePing.originator = PINGERFalse;
|
||
|
activePing.ID = ID;
|
||
|
activePing.expectedTrip = 3;
|
||
|
activePing.timestamp = current_time();
|
||
|
activePing.timeout = (activePing.timestamp + PI_TRIP2_TIMEOUT);
|
||
|
activePing.remoteIP = from->sin_addr.s_addr;
|
||
|
activePing.remotePort = from->sin_port;
|
||
|
activePing.reply = NULL;
|
||
|
activePing.replyParam = NULL;
|
||
|
|
||
|
// Add it to the list.
|
||
|
//////////////////////
|
||
|
ArrayAppend(piActivePingList, &activePing);
|
||
|
}
|
||
|
|
||
|
GSI_UNUSED(data);
|
||
|
GSI_UNUSED(recvTime);
|
||
|
}
|
||
|
|
||
|
static void piProcessTrip2(piUDPPing * udpPing, const char * data, SOCKADDR_IN * from, gsi_time recvTime)
|
||
|
{
|
||
|
char dataOut[PI_DATA_MAX_LEN];
|
||
|
int index;
|
||
|
piActivePing * activePing;
|
||
|
|
||
|
// Validity check.
|
||
|
// ID_A != 0 was triggered by a ping from 205.251.192.203.
|
||
|
// 12/4/00 at approx. 5pm
|
||
|
// This is the same IP as the shifted ping assert above.
|
||
|
//////////////////////////////////////////////////////////
|
||
|
assert(udpPing->trip == 2);
|
||
|
assert(udpPing->ID_A != 0);
|
||
|
if(udpPing->ID_A == 0)
|
||
|
return;
|
||
|
|
||
|
// Were we waiting for this?
|
||
|
////////////////////////////
|
||
|
activePing = piFindActivePing(udpPing->ID_A, &index);
|
||
|
if(activePing == NULL)
|
||
|
return;
|
||
|
|
||
|
// Setup the data.
|
||
|
//////////////////
|
||
|
memset(dataOut, 0, PI_DATA_MAX_LEN);
|
||
|
if(piPingerSetData != NULL)
|
||
|
{
|
||
|
piSettingData = PINGERTrue;
|
||
|
piPingerSetData(from->sin_addr.s_addr, from->sin_port, dataOut, PI_DATA_MAX_LEN, piPingerSetDataParam);
|
||
|
piSettingData = PINGERFalse;
|
||
|
}
|
||
|
|
||
|
// Do we send a reply?
|
||
|
//////////////////////
|
||
|
if(udpPing->ID_B != 0)
|
||
|
{
|
||
|
// Send the return ping.
|
||
|
////////////////////////
|
||
|
piSendPing(from, 3, 0, udpPing->ID_B, dataOut);
|
||
|
}
|
||
|
|
||
|
// Call the callback?
|
||
|
/////////////////////
|
||
|
if(activePing->reply != NULL)
|
||
|
{
|
||
|
// Calculate the ping.
|
||
|
//////////////////////
|
||
|
int ping = piCalculatePing(activePing->timestamp, recvTime);
|
||
|
|
||
|
// Add the callback.
|
||
|
////////////////////
|
||
|
piQueueCallback(from->sin_addr.s_addr, from->sin_port, ping, data, PI_DATA_MAX_LEN, activePing->replyParam, activePing->reply);
|
||
|
}
|
||
|
|
||
|
// gsifree the active ping.
|
||
|
////////////////////////
|
||
|
ArrayDeleteAt(piActivePingList, index);
|
||
|
}
|
||
|
|
||
|
static void piProcessTrip3(piUDPPing * udpPing, const char * data, SOCKADDR_IN * from, gsi_time recvTime)
|
||
|
{
|
||
|
int index;
|
||
|
piActivePing * activePing;
|
||
|
|
||
|
// Validity check.
|
||
|
//////////////////
|
||
|
assert(udpPing->trip == 3);
|
||
|
assert(udpPing->ID_A == 0);
|
||
|
if(udpPing->ID_A != 0)
|
||
|
return;
|
||
|
assert(udpPing->ID_B != 0);
|
||
|
if(udpPing->ID_B == 0)
|
||
|
return;
|
||
|
|
||
|
// Were we waiting for this?
|
||
|
////////////////////////////
|
||
|
activePing = piFindActivePing(udpPing->ID_B, &index);
|
||
|
if(activePing == NULL)
|
||
|
return;
|
||
|
|
||
|
// Call the callback.
|
||
|
/////////////////////
|
||
|
assert(piPingerPinged != NULL);
|
||
|
if(piPingerPinged != NULL)
|
||
|
{
|
||
|
// Calculate the ping.
|
||
|
//////////////////////
|
||
|
int ping = piCalculatePing(activePing->timestamp, recvTime);
|
||
|
|
||
|
// Call it.
|
||
|
///////////
|
||
|
piPingerPinged(from->sin_addr.s_addr, from->sin_port, ping, data, PI_DATA_MAX_LEN, piPingerPingedParam);
|
||
|
}
|
||
|
|
||
|
// gsifree the active ping.
|
||
|
////////////////////////
|
||
|
ArrayDeleteAt(piActivePingList, index);
|
||
|
}
|
||
|
|
||
|
static void piProcessPing(piUDPPing * udpPing, const char * data, SOCKADDR_IN * from, gsi_time recvTime)
|
||
|
{
|
||
|
assert(udpPing != NULL);
|
||
|
assert(data != NULL);
|
||
|
assert(from != NULL);
|
||
|
|
||
|
// Process based on trip num.
|
||
|
/////////////////////////////
|
||
|
if(udpPing->trip == 1)
|
||
|
piProcessTrip1(udpPing, data, from, recvTime);
|
||
|
else if(udpPing->trip == 2)
|
||
|
piProcessTrip2(udpPing, data, from, recvTime);
|
||
|
else if(udpPing->trip == 3)
|
||
|
piProcessTrip3(udpPing, data, from, recvTime);
|
||
|
}
|
||
|
|
||
|
static void piProcessIncoming(void)
|
||
|
{
|
||
|
int rcode;
|
||
|
unsigned char buffer[PINGER_UDP_PING_SIZE];
|
||
|
SOCKADDR_IN from;
|
||
|
int len;
|
||
|
gsi_time recvTime;
|
||
|
piUDPPing udpPing;
|
||
|
char data[PI_DATA_MAX_LEN];
|
||
|
|
||
|
// Check for incoming dgrams.
|
||
|
/////////////////////////////
|
||
|
while(piInitialized && CanReceiveOnSocket(piSocket))
|
||
|
{
|
||
|
// Read the dgram.
|
||
|
//////////////////
|
||
|
len = sizeof(SOCKADDR_IN);
|
||
|
|
||
|
rcode = recvfrom(piSocket, (char *)buffer, PINGER_UDP_PING_SIZE, 0, (SOCKADDR *)&from, &len);
|
||
|
|
||
|
// Check for an error.
|
||
|
//////////////////////
|
||
|
if (gsiSocketIsError(rcode))
|
||
|
{
|
||
|
if(GOAGetLastError(piSocket) == WSAEMSGSIZE)
|
||
|
{
|
||
|
// Ignore "too big" errors.
|
||
|
///////////////////////////
|
||
|
rcode = PINGER_UDP_PING_SIZE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Something's wrong, get the hell out of here!
|
||
|
///////////////////////////////////////////////
|
||
|
return; //ERRCON
|
||
|
}
|
||
|
}
|
||
|
else if(rcode < PINGER_UDP_PING_SIZE)
|
||
|
{
|
||
|
// The ping was too small.
|
||
|
//////////////////////////
|
||
|
return; //ERRCON
|
||
|
}
|
||
|
|
||
|
// What time did we receive it?
|
||
|
///////////////////////////////
|
||
|
recvTime = current_time();
|
||
|
|
||
|
// Convert the buffer into a ping.
|
||
|
//////////////////////////////////
|
||
|
if(piBytesToPing(buffer, &udpPing, data))
|
||
|
{
|
||
|
// Process the dgram.
|
||
|
/////////////////////
|
||
|
piProcessPing(&udpPing, data, &from, recvTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Last time we checked for incoming data.
|
||
|
//////////////////////////////////////////
|
||
|
piLastThinkTime = current_time();
|
||
|
}
|
||
|
|
||
|
static void piCheckTimeouts(void)
|
||
|
{
|
||
|
gsi_time now;
|
||
|
piActivePing * activePing;
|
||
|
int len;
|
||
|
int n;
|
||
|
|
||
|
// Get the number of active pings.
|
||
|
//////////////////////////////////
|
||
|
len = ArrayLength(piActivePingList);
|
||
|
|
||
|
// Get out if no active pings.
|
||
|
//////////////////////////////
|
||
|
if(len == 0)
|
||
|
return;
|
||
|
|
||
|
// Get the current time.
|
||
|
////////////////////////
|
||
|
now = current_time();
|
||
|
|
||
|
// Go through the list backwards.
|
||
|
/////////////////////////////////
|
||
|
for(n = (len - 1) ; n >= 0 ; n--)
|
||
|
{
|
||
|
// Get the nth element of the list.
|
||
|
///////////////////////////////////
|
||
|
activePing = (piActivePing *)ArrayNth(piActivePingList, n);
|
||
|
assert(activePing != NULL);
|
||
|
if(activePing != NULL)
|
||
|
{
|
||
|
// Does it have a timeout?
|
||
|
//////////////////////////
|
||
|
if(activePing->timeout != 0)
|
||
|
{
|
||
|
// Has it timed out?
|
||
|
////////////////////
|
||
|
if(activePing->timeout <= now)
|
||
|
{
|
||
|
// Do we need to do a failed callback?
|
||
|
//////////////////////////////////////
|
||
|
if((activePing->originator) && (activePing->reply != NULL))
|
||
|
{
|
||
|
// Queue the callback.
|
||
|
//////////////////////
|
||
|
piQueueCallback(activePing->remoteIP, activePing->remotePort, PINGER_TIMEOUT, NULL, 0, activePing->replyParam, activePing->reply);
|
||
|
}
|
||
|
|
||
|
// Kill it!
|
||
|
///////////
|
||
|
ArrayDeleteAt(piActivePingList, n);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pingerThink(void)
|
||
|
{
|
||
|
// If not initialized, don't do anything.
|
||
|
/////////////////////////////////////////
|
||
|
if(!piInitialized)
|
||
|
return;
|
||
|
|
||
|
// Check for setting data.
|
||
|
//////////////////////////
|
||
|
if(piSettingData)
|
||
|
return;
|
||
|
|
||
|
// Process incoming data.
|
||
|
/////////////////////////
|
||
|
piProcessIncoming();
|
||
|
|
||
|
// Check for timeouts.
|
||
|
//////////////////////
|
||
|
piCheckTimeouts();
|
||
|
|
||
|
// Call queued callbacks.
|
||
|
/////////////////////////
|
||
|
piCallCallbacks();
|
||
|
}
|
||
|
|
||
|
void pingerPing(unsigned int IP,
|
||
|
unsigned short port,
|
||
|
pingerGotPing reply,
|
||
|
void * replyParam,
|
||
|
PINGERBool blocking,
|
||
|
gsi_time timeout)
|
||
|
{
|
||
|
SOCKADDR_IN to;
|
||
|
uint16 ID;
|
||
|
|
||
|
assert(piInitialized);
|
||
|
assert(IP != 0);
|
||
|
|
||
|
// ICMP not supported yet!
|
||
|
//////////////////////////
|
||
|
assert(port != 0);
|
||
|
assert(piUDPEnabled);
|
||
|
assert(piSocket != INVALID_SOCKET);
|
||
|
|
||
|
// Check for setting data.
|
||
|
//////////////////////////
|
||
|
if(piSettingData)
|
||
|
return;
|
||
|
|
||
|
// Construct the outgoing address.
|
||
|
//////////////////////////////////
|
||
|
memset(&to, 0, sizeof(SOCKADDR_IN));
|
||
|
to.sin_family = AF_INET;
|
||
|
to.sin_port = htons(port);
|
||
|
to.sin_addr.s_addr = IP;
|
||
|
|
||
|
// Get an ID for this ping.
|
||
|
///////////////////////////
|
||
|
ID = piGetNextID();
|
||
|
|
||
|
// Send the outgoing ping.
|
||
|
//////////////////////////
|
||
|
if(piSendPing(&to, 1, ID, 0, NULL))
|
||
|
{
|
||
|
piActivePing activePing;
|
||
|
|
||
|
// Setup the active ping object.
|
||
|
////////////////////////////////
|
||
|
activePing.originator = PINGERTrue;
|
||
|
activePing.ID = ID;
|
||
|
activePing.expectedTrip = 2;
|
||
|
activePing.timestamp = current_time();
|
||
|
if(timeout == 0)
|
||
|
activePing.timeout = 0;
|
||
|
else
|
||
|
activePing.timeout = (activePing.timestamp + timeout);
|
||
|
activePing.remoteIP = IP;
|
||
|
activePing.remotePort = port;
|
||
|
activePing.reply = reply;
|
||
|
activePing.replyParam = replyParam;
|
||
|
|
||
|
// Add it to the list.
|
||
|
//////////////////////
|
||
|
ArrayAppend(piActivePingList, &activePing);
|
||
|
}
|
||
|
|
||
|
// Is this blocking?
|
||
|
////////////////////
|
||
|
if(blocking)
|
||
|
{
|
||
|
// Keep going until this ping has been de-listed.
|
||
|
/////////////////////////////////////////////////
|
||
|
while(piFindActivePing(ID, NULL) != NULL)
|
||
|
{
|
||
|
// Think.
|
||
|
/////////
|
||
|
pingerThink();
|
||
|
|
||
|
// Yield.
|
||
|
/////////
|
||
|
msleep(1);
|
||
|
}
|
||
|
|
||
|
// Call callbacks.
|
||
|
//////////////////
|
||
|
piCallCallbacks();
|
||
|
}
|
||
|
}
|