Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

1130 lines
36 KiB
C

/*H*************************************************************************************************/
/*!
\File protomangle.c
\Description
This module encapsulates client services for use of the EA.Com Demangler service.
\Copyright
Copyright (c) Tiburon Entertainment / Electronic Arts 2003. ALL RIGHTS RESERVED.
\Version 1.0 04/03/03 (JLB) First Version
*/
/*************************************************************************************************H*/
/*** Include files *********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DirtySDK/dirtysock.h"
#include "DirtySDK/dirtysock/dirtycert.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/proto/protohttp.h"
#include "DirtySDK/proto/protomangle.h"
/*** Defines ***************************************************************************/
#define PROTOMANGLE_VERBOSE (DIRTYCODE_DEBUG && TRUE)
#define PROTOMANGLE_RAND_PORTBASE (2000)
//! range used for random port generation (used to be 8000 but that proved to conflict with Sony port range restriction on PS4)
#define PROTOMANGLE_RAND_PORTRANGE (6000)
#define PROTOMANGLE_HTTP_BUFSIZE (1024)
#define PROTOMANGLE_HTTP_TIMEOUT (60 * 1000)
/*** Macros ****************************************************************************/
/*** Type Definitions ******************************************************************/
typedef enum ProtoMangleResponseE
{
PROTOMANGLE_RESP_INVALID = -1,
PROTOMANGLE_RESP_SUCCESS,
PROTOMANGLE_RESP_PROBE,
PROTOMANGLE_RESP_FAILURE,
PROTOMANGLE_NUMRESP
} ProtoMangleResponseE;
//! parsed probe request data
typedef struct ProtoMangleProbeT
{
int32_t iCurCnt; //!< current probe iteration
int32_t iSndCnt; //!< number of probes to send
int32_t iAddr; //!< peer address
int32_t iPort; //!< peer port
int32_t iSrcPort; //!< local port used to bind (if -1, bind a random port)
int32_t iId; //!< transaction ID
char strTag[64]; //!< response tag
} ProtoMangleProbeT;
//! internal module state
struct ProtoMangleRefT //!< module state
{
ProtoHttpRefT *pHttp; //!< http module
SocketT *pProbeSock; //!< socket for sending UDP probes
SocketT *pSharedProbeSock; //!< shared socket, if any
//! module memory group
int32_t iMemGroup;
void *pMemGroupUserData;
int32_t iHostAddr; //!< local address
int32_t iGamePort; //!< game port
int32_t iPeerAddr; //!< peer address (when eMangleState==ST_MNGL_DONE)
int32_t iPeerPort; //!< peer port to connect to (when eMangleState==ST_MNGL_DONE)
char strCookie[PROTOMANGLE_STRCOOKIE_MAX]; //!< session cookie
char strGameID[PROTOMANGLE_STRGAMEID_MAX]; //!< game feature ID
char strLKey[PROTOMANGLE_STRLKEY_MAX]; //!< user LKey
char strServer[PROTOMANGLE_STRSERVER_MAX]; //!< server name
int32_t iServerPort; //!< server port
ProtoMangleResponseE eResponse; //!< most recent server response
int32_t iRandPort; //!< 'random' port to bind if no local port requested
enum
{
ST_MNGL_IDLE, //!< created
ST_MNGL_WAIT, //!< waiting for response from HTTP server
ST_MNGL_DONE, //!< successful completion
ST_MNGL_FAIL, //!< failure condition
ST_MNGL_REPT //!< reporting
} eMangleState;
int32_t iRecvSize; //!< current receive size
char strBuf[PROTOMANGLE_HTTP_BUFSIZE];
};
/*** Function Prototypes ***************************************************************/
/*** Variables *************************************************************************/
// Private variables
//! response codes
static const char *_ProtoMangle_pResponseTable[PROTOMANGLE_NUMRESP] =
{
"success", // PROTOMANGLE_RESP_SUCCESS
"probe", // PROTOMANGLE_RESP_PROBE
"failure" // PROTOMANGLE_RESP_FAILURE
};
//! report codes
static const char *_ProtoMangle_pReportTable[PROTOMANGLE_NUMSTATUS] =
{
"connected", // PROTOMANGLE_STATUS_CONNECTED
"failed" // PROTOMANGLE_STATUS_FAILED
};
// Public variables
/*** Private Functions *****************************************************************/
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleSockClose
\Description
Safe socket close of a probe socket
\Input *pRef - module state
\Input bRelease - whether shared probe socket should be release or not
\Version 01/14/05 (JLB)
*/
/*************************************************************************************************F*/
static void _ProtoMangleSockClose(ProtoMangleRefT *pRef, uint32_t bRelease)
{
if (pRef->pProbeSock != NULL)
{
if (pRef->pProbeSock != pRef->pSharedProbeSock)
{
NetPrintf(("protomangle: [%p] closing probe socket %p\n", pRef, pRef->pProbeSock));
SocketShutdown(pRef->pProbeSock, SOCK_NOSEND);
SocketClose(pRef->pProbeSock);
pRef->pProbeSock = NULL;
}
else if (bRelease)
{
NetPrintf(("protomangle: [%p] releasing shared probe socket %p\n", pRef, pRef->pProbeSock));
SocketRelease(pRef->pProbeSock);
pRef->pProbeSock = pRef->pSharedProbeSock = NULL;
}
}
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleHttpPost
\Description
Send a POST request to HTTP server.
\Input *pRef - module state
\Input *pUrl - url to post to
\Input *pCookie - session cookie
\Input *pData - POST data
\Output
int32_t - zero=success, negative=failure
\Version 07/18/03 (JLB)
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleHttpPost(ProtoMangleRefT *pRef, const char *pUrl, const char *pCookie, const char *pData)
{
char strBuffer[256];
// setup the extra header fields
ds_snzprintf(strBuffer, sizeof(strBuffer),
"Cookie: sessionID=%s\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n",
pCookie);
// set extra header fields
ProtoHttpControl(pRef->pHttp, 'apnd', 0, 0, strBuffer);
// setup the url
ds_snzprintf(strBuffer, sizeof(strBuffer), "http://%s:%d/%s", pRef->strServer, pRef->iServerPort, pUrl);
// execute the post
return(ProtoHttpPost(pRef->pHttp, strBuffer, pData, (int32_t)strlen(pData), FALSE));
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleHttpRequest
\Description
Issue HTTP request to the demangler server.
\Input *pRef - module state
\Version 1.0 04/08/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static void _ProtoMangleHttpRequest(ProtoMangleRefT *pRef)
{
char strAppend[256], strUrl[256 + (3 * DIRTYCERT_SERVICENAME_SIZE)], strAddrText[20], strServiceName[DIRTYCERT_SERVICENAME_SIZE];
int32_t iFormatted = 0;
if (DirtyCertStatus('snam', strServiceName, DIRTYCERT_SERVICENAME_SIZE) < 0)
{
ds_snzprintf(strServiceName, sizeof(strServiceName), "invalid");
}
// setup the url
iFormatted = ds_snzprintf(strUrl, sizeof(strUrl), "http://%s:%d/getPeerAddress?myIP=%s&myPort=%d&version=1.0",
pRef->strServer, pRef->iServerPort, SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), pRef->iGamePort);
// add service name
ProtoHttpUrlEncodeStrParm(strUrl + iFormatted, sizeof(strUrl) - iFormatted, "&gameID=", strServiceName);
// setup the extra header fields
ds_snzprintf(strAppend, sizeof(strAppend), "Cookie: sessionID=%s\r\n", pRef->strCookie);
// set extra header fields
ProtoHttpControl(pRef->pHttp, 'apnd', 0, 0, strAppend);
// send the request
ProtoHttpGet(pRef->pHttp, strUrl, FALSE);
// set state
pRef->iRecvSize = 0;
pRef->eMangleState = ST_MNGL_WAIT;
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleGetParm
\Description
Parse a parameter from a demangler server response.
\Input *pDst - pointer to buffer for parsed parm
\Input iBufLen - length of destination buffer
\Input *pSrc - pointer to server reseponse
\Input *pParmName - name of parameter to parse
\Input bParseCount - if TRUE, parse response count
\Output
int32_t - response count (one if bParseCount==FALSE), or negative if parmname not found
\Version 1.0 04/08/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleGetParm(char *pDst, int32_t iBufLen, const char *pSrc, const char *pParmName, int32_t bParseCount)
{
const char *pParm;
int32_t iStr, iId = -1;
// clear the destination buffer
pDst[0] = '\0';
// make sure we have room for terminating NULL character
iBufLen--;
// look for parm
if ((pParm = strstr(pSrc, pParmName)) != NULL)
{
// parse count?
if (bParseCount == TRUE)
{
char *pId;
if ((pId = strchr(pParm, '-')) != NULL)
{
iId = pId[1] - '0';
if ((iId < 0) || (iId > 9))
{
iId = -1;
}
}
}
else
{
iId = TRUE;
}
// look for value
if ((pParm = strchr(pParm, '=')) != NULL)
{
// found string; make a copy of it
for (iStr = 0, pParm++; (iStr < iBufLen) && (pParm[iStr] != '\r') && (pParm[iStr] != '\n'); iStr++)
{
pDst[iStr] = pParm[iStr];
}
// NULL terminate
pDst[iStr++] = '\0';
}
}
return(iId);
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleParseAdvance
\Description
Advance past current parm block
\Input *pSrc - pointer to current block
\Input *pTerminator - termination character
\Output
const char* - pointer to next block, or NULL if there is none
\Notes
Parm blocks are terminated by double LFs.
\Version 1.0 04/09/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static const char *_ProtoMangleParseAdvance(const char *pSrc, const char *pTerminator)
{
if ((pSrc = strstr(pSrc, pTerminator)) != NULL)
{
while(*pSrc == '\n')
{
pSrc++;
}
}
return(pSrc);
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleParseResponseType
\Description
Determine type of demangler server response.
\Input *pSrc - module state
\Input *pResponse - storage for parsed response type
\Output
const char * - pointer to next entry
\Version 1.0 04/08/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static const char *_ProtoMangleParseResponseType(const char *pSrc, ProtoMangleResponseE *pResponse)
{
char strCmd[32];
int32_t iResp;
*pResponse = PROTOMANGLE_RESP_INVALID;
if (_ProtoMangleGetParm(strCmd, sizeof(strCmd), pSrc, "status", FALSE))
{
for (iResp = 0; iResp < PROTOMANGLE_NUMRESP; iResp++)
{
if (!strncmp(strCmd, _ProtoMangle_pResponseTable[iResp], strlen(_ProtoMangle_pResponseTable[iResp])))
{
*pResponse = (ProtoMangleResponseE)iResp;
break;
}
}
}
return(_ProtoMangleParseAdvance(pSrc, "\n"));
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleParseSuccess
\Description
Parse demangler server 'success' response.
\Input *pRef - module state
\Input *pStr - server response string
\Output
int32_t - TRUE if successfully parsed, else FALSE
\Version 1.0 04/09/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleParseSuccess(ProtoMangleRefT *pRef, const char *pStr)
{
char strParm[32];
int32_t bLinesParsed = 0;
if ((_ProtoMangleGetParm(strParm, sizeof(strParm), pStr, "peerIP", FALSE)) >= 0)
{
if ((pRef->iPeerAddr = SocketInTextGetAddr(strParm)) != 0)
{
bLinesParsed++;
}
}
if ((_ProtoMangleGetParm(strParm, sizeof(strParm), pStr, "peerPort", FALSE)) >= 0)
{
pRef->iPeerPort = atoi(strParm);
bLinesParsed++;
}
return(bLinesParsed == 2);
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleParseProbeRequest
\Description
Parse a 'probe' demangler server request.
\Input *pProbe - pointer to probe structure to fill in
\Input *pRef - module ref
\Input *pSrc - server response to parse
\Output
int32_t - TRUE if response parsed, else FALSE
\Version 1.0 04/08/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleParseProbeRequest(ProtoMangleProbeT *pProbe, ProtoMangleRefT *pRef, const char *pSrc)
{
int32_t bLinesParsed = 0, iParm;
char strParm[64];
if (pSrc == NULL)
{
return(FALSE);
}
// init
pProbe->iCurCnt = 0;
pProbe->iSrcPort = -1;
pProbe->iId = -1;
// parse target address
if ((pProbe->iId = _ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "targetIP", TRUE)) >= 0)
{
if ((pProbe->iAddr = SocketInTextGetAddr(strParm)) != 0)
{
bLinesParsed++;
}
}
if (pProbe->iId < 0)
{
return(FALSE);
}
// parse target port
if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "targetPort", TRUE) == pProbe->iId)
{
if ((pProbe->iPort = atoi(strParm)) > 0)
{
bLinesParsed++;
}
}
// parse request tag
if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "tag", TRUE) == pProbe->iId)
{
ds_strnzcpy(pProbe->strTag, strParm, sizeof(pProbe->strTag));
bLinesParsed++;
}
// parse send count
if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "sendCount", TRUE) == pProbe->iId)
{
if (((iParm = atoi(strParm)) > 1) && (iParm < 32))
{
pProbe->iSndCnt = iParm;
bLinesParsed++;
}
}
// parse source port (optional)
if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "sourcePort", TRUE) == pProbe->iId)
{
if ((iParm = atoi(strParm)) > 0)
{
#ifdef DIRTYCODE_PS4
if (iParm > 32767)
{
int32_t iParmOriginal = iParm;
iParm &= 36863;
if (iParmOriginal != iParm)
{
NetPrintf(("protomangle: using port %d instead of %d.\n", iParm, iParmOriginal));
}
}
#endif
pProbe->iSrcPort = iParm;
}
}
return(bLinesParsed == 4);
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleSetupProbeSocket
\Description
Bind probe socket.
\Input *pRef - module state
\Input *pProbe - probe parameters
\Output
int32_t - local port bound to socket, or negative on error
\Version 1.0 04/09/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleSetupProbeSocket(ProtoMangleRefT *pRef, ProtoMangleProbeT *pProbe)
{
struct sockaddr BindAddr;
// tear down previous socket if we're not using shared socket
_ProtoMangleSockClose(pRef, FALSE);
// if we have a shared socket and this is a request for the shared socket port, set it and return
if ((pRef->pSharedProbeSock != NULL) && (pProbe->iSrcPort == pRef->iGamePort))
{
NetPrintf(("protomangle: [%p] using shared socket ref %p to probe port %d\n", pRef, pRef->pSharedProbeSock, pRef->iGamePort));
pRef->pProbeSock = pRef->pSharedProbeSock;
}
else
{
// recreate probe socket
if ((pRef->pProbeSock = SocketOpen(AF_INET, SOCK_DGRAM, 0)) == NULL)
{
NetPrintf(("protomangle: [%p] error creating probe socket\n", pRef));
return(-1);
}
// set up local binding
SockaddrInit(&BindAddr, AF_INET);
if (pProbe->iSrcPort != -1)
{
SockaddrInSetPort(&BindAddr, pProbe->iSrcPort);
}
else
{
SockaddrInSetPort(&BindAddr, pRef->iRandPort++);
}
// bind locally
if (SocketBind(pRef->pProbeSock, &BindAddr, sizeof(BindAddr)) == SOCKERR_NONE)
{
NetPrintf(("protomangle: [%p] created probe socket %p bound to port %d\n", pRef, pRef->pProbeSock, SockaddrInGetPort(&BindAddr)));
}
else
{
NetPrintf(("protomangle: [%p] error binding probe socket %p to port %d\n", pRef, pRef->pProbeSock, SockaddrInGetPort(&BindAddr)));
// tear down socket since binding failed
_ProtoMangleSockClose(pRef, FALSE);
return(-1);
}
}
// return local port to caller
SocketInfo(pRef->pProbeSock, 'bind', 0, &BindAddr, sizeof(BindAddr));
return(SockaddrInGetPort(&BindAddr));
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleSendProbe
\Description
Send UDP probe packet(s) to demangler server.
\Input *pRef - module state
\Input *pProbe - probe parameters
\Input iSrcPort - source port
\Input iProbeReq - probe request count from current request list
\Version 04/08/03 (JLB)
*/
/*************************************************************************************************F*/
static void _ProtoMangleSendProbe(ProtoMangleRefT *pRef, ProtoMangleProbeT *pProbe, int32_t iSrcPort, int32_t iProbeReq)
{
struct sockaddr PeerAddr;
char strMesg[128], strAddrText[20];
int32_t iErr, iMsgSize;
// set up target
SockaddrInit(&PeerAddr, AF_INET);
SockaddrInSetAddr(&PeerAddr, pProbe->iAddr);
SockaddrInSetPort(&PeerAddr, pProbe->iPort);
// format probe package
ds_snzprintf(strMesg, sizeof(strMesg), "sourceIP=%s\r\nsourcePort=%d\r\ntag=%s\r\nsendCount=%d\r\n",
SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), iSrcPort, pProbe->strTag, pProbe->iCurCnt);
iMsgSize = (int32_t)strlen(strMesg)+1;
// send probe
iErr = SocketSendto(pRef->pProbeSock, strMesg, iMsgSize, 0, &PeerAddr, sizeof(PeerAddr));
#if PROTOMANGLE_VERBOSE
if (iErr == iMsgSize)
{
NetPrintf(("protomangle: [%p] success ", pRef));
}
else
{
NetPrintf(("protomangle: [%p] error %d ", pRef, iErr));
}
NetPrintf(("sending probe %d/%d (%d) from port %d\n", pProbe->iCurCnt, pProbe->iSndCnt, iProbeReq, iSrcPort));
#else
(void)iErr;
#endif
}
/*F*************************************************************************************************/
/*!
\Function _ProtoMangleHandleProbeRequest
\Description
Parse a probe request block, and issue any probes requested.
\Input *pRef - module state
\Input *pSrc - probe request string
\Output
int32_t - zero on success, negative on failure
\Version 1.0 04/08/03 (JLB) First Version
*/
/*************************************************************************************************F*/
static int32_t _ProtoMangleHandleProbeRequest(ProtoMangleRefT *pRef, const char *pSrc)
{
ProtoMangleProbeT Probe;
int32_t iProbeReq, iSrcPort;
// parse probe request list,
for (iProbeReq = 1; _ProtoMangleParseProbeRequest(&Probe, pRef, pSrc) != FALSE; iProbeReq++)
{
// make sure sequence IDs match
if (iProbeReq != Probe.iId)
{
NetPrintf(("protomangle: [%p] warning, probe sequence mismatch\n", pRef));
}
// init probe socket
if ((iSrcPort = _ProtoMangleSetupProbeSocket(pRef, &Probe)) < 0)
{
return(-1);
}
// send iSndCnt probes
for (Probe.iCurCnt = 0; Probe.iCurCnt < Probe.iSndCnt; Probe.iCurCnt++)
{
_ProtoMangleSendProbe(pRef, &Probe, iSrcPort, iProbeReq);
}
// advance to next response block
pSrc = _ProtoMangleParseAdvance(pSrc,"\n\n");
}
return(0);
}
/*** Public Functions ******************************************************************/
/*F*************************************************************************************************/
/*!
\Function ProtoMangleCreate
\Description
Allocate module state and prepare for use
\Input *pServer - pointer to server name
\Input iPort - server port
\Input *pGameID - pointer to game feature ID string
\Input *pLKey - pointer to user LKey
\Output
ProtoMangleRefT * - reference pointer (must be passed to all other functions)
\Version 1.0 04/03/03 (JLB) First Version
\Version 1.1 06/16/03 (JLB) Added server parms as arguments
*/
/*************************************************************************************************F*/
ProtoMangleRefT *ProtoMangleCreate(const char *pServer, int32_t iPort, const char *pGameID, const char *pLKey)
{
ProtoMangleRefT *pRef;
int32_t iMemGroup;
void *pMemGroupUserData;
// Query current mem group data
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
NetPrintf(("protomangle: server:%s port:%d gameid:%s lkey:%s\n", pServer, iPort, pGameID, pLKey));
// allocate and init module state
if ((pRef = DirtyMemAlloc(sizeof(*pRef), PROTOMANGLE_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
{
NetPrintf(("protomangle: could not allocate module state\n"));
return(NULL);
}
ds_memclr(pRef, sizeof(*pRef));
pRef->iMemGroup = iMemGroup;
pRef->pMemGroupUserData = pMemGroupUserData;
// create http module
if ((pRef->pHttp = ProtoHttpCreate(PROTOMANGLE_HTTP_BUFSIZE)) == NULL)
{
DirtyMemFree(pRef, PROTOMANGLE_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
return(NULL);
}
// since we know we are going to have an ongoing transaction, enable keep-alive from the beginning
ProtoHttpControl(pRef->pHttp, 'keep', 1, 0, NULL);
// set default http timeout
ProtoHttpControl(pRef->pHttp, 'time', PROTOMANGLE_HTTP_TIMEOUT, 0, NULL);
// generate 'random' port in range
pRef->iRandPort = (NetTick() % PROTOMANGLE_RAND_PORTRANGE) + PROTOMANGLE_RAND_PORTBASE;
// save server parms, gameID, LKey
ds_strnzcpy(pRef->strServer, pServer, sizeof(pRef->strServer));
pRef->iServerPort = iPort;
ds_strnzcpy(pRef->strGameID, pGameID, sizeof(pRef->strGameID));
ds_strnzcpy(pRef->strLKey, pLKey, sizeof(pRef->strLKey));
// return ref to caller
return(pRef);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleDestroy
\Description
Destroy the module and release its state
\Input *pRef - reference pointer
\Version 1.0 04/03/03 (JLB) First Version
*/
/*************************************************************************************************F*/
void ProtoMangleDestroy(ProtoMangleRefT *pRef)
{
NetPrintf(("protomangle: [%p] shutting down\n", pRef));
// close probe socket, if we own it, else release it
_ProtoMangleSockClose(pRef, TRUE);
// shut down http module
ProtoHttpDestroy(pRef->pHttp);
// release ref
DirtyMemFree(pRef, PROTOMANGLE_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleConnect2
\Description
Initiate transaction with demangler service
\Input *pRef - module state
\Input iMyPort - game port
\Input *pSessID - pointer to unique session ID (must be identical between clients)
\Input iSessIDLen - lenght of SessID string
\Version 01/16/13 mclouatre
*/
/*************************************************************************************************F*/
void ProtoMangleConnect2(ProtoMangleRefT *pRef, int32_t iMyPort, const char *pSessID, int32_t iSessIDLen)
{
// save port/addr
pRef->iHostAddr = SocketGetLocalAddr();
pRef->iGamePort = iMyPort;
// save cookie
ds_strnzcpy(pRef->strCookie, pSessID, sizeof(pRef->strCookie));
/* close previous http socket to prevent http connections spanning
sessions, which doesn't work with the demangler load balancer */
ProtoHttpControl(pRef->pHttp, 'disc', 0, 0, NULL);
// fire off initial request
_ProtoMangleHttpRequest(pRef);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleConnect
\Description
Initiate transaction with demangler service
\Input *pRef - module state
\Input iMyPort - game port
\Input *pSessID - pointer to unique session ID (must be identical between clients)
\Version 04/07/03 jbrookes
*/
/*************************************************************************************************F*/
void ProtoMangleConnect(ProtoMangleRefT *pRef, int32_t iMyPort, const char *pSessID)
{
ProtoMangleConnect2(pRef, iMyPort, pSessID, (int32_t)strlen(pSessID));
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleConnectSocket
\Description
Initiate transaction with demangler service, using the given shared socket ref for
UDP probes that are to be sent out over the game port.
\Input *pRef - module state
\Input uSockRef - socket reference to use for UDP probes sent over game port
\Input *pSessID - pointer to unique session ID (must be identical between clients)
\Version 1.0 08/24/04 (JLB) First Version
*/
/*************************************************************************************************F*/
void ProtoMangleConnectSocket(ProtoMangleRefT *pRef, intptr_t uSockRef, const char *pSessID)
{
struct sockaddr SockAddr;
// import socket
pRef->pSharedProbeSock = SocketImport(uSockRef);
if (pRef->pSharedProbeSock == NULL)
{
NetPrintf(("protomangle: [%p] unable to import sockref 0x%08x\n", pRef, uSockRef));
return;
}
// save addr and socket ref
pRef->iHostAddr = SocketGetLocalAddr();
// look up and save bind port
SocketInfo(pRef->pSharedProbeSock, 'bind', 0, &SockAddr, sizeof(SockAddr));
pRef->iGamePort = SockaddrInGetPort(&SockAddr);
NetPrintf(("protomangle: [%p] created shared socket using platform socket ref %p bound to port %d\n", pRef, pRef->pSharedProbeSock, pRef->iGamePort));
// save cookie
ds_strnzcpy(pRef->strCookie, pSessID, sizeof(pRef->strCookie));
/* close previous http socket to prevent http connections spanning
sessions, which doesn't work with the demangler load balancer */
ProtoHttpControl(pRef->pHttp, 'disc', 0, 0, NULL);
// fire off initial reques
_ProtoMangleHttpRequest(pRef);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleUpdate
\Description
Update module state
\Input *pRef - module reference
\Version 04/07/03 (JLB)
*/
/*************************************************************************************************F*/
void ProtoMangleUpdate(ProtoMangleRefT *pRef)
{
// handle updates to connect process
if (pRef->eMangleState == ST_MNGL_WAIT)
{
int32_t iRecvLen;
// update HTTP request
ProtoHttpUpdate(pRef->pHttp);
// poll for completion
if ((iRecvLen = ProtoHttpRecvAll(pRef->pHttp, pRef->strBuf, sizeof(pRef->strBuf))) >= 0)
{
const char *pStr;
// get data
NetPrintf(("protomangle: [%p] server response\n", pRef));
NetPrintWrap(pRef->strBuf, 80);
// parse response type
pStr = _ProtoMangleParseResponseType(pRef->strBuf, &pRef->eResponse);
switch(pRef->eResponse)
{
case PROTOMANGLE_RESP_SUCCESS:
// parse success result
pRef->eMangleState = _ProtoMangleParseSuccess(pRef, pStr) ? ST_MNGL_DONE : ST_MNGL_FAIL;
break;
case PROTOMANGLE_RESP_PROBE:
// parse and issue probes
if (_ProtoMangleHandleProbeRequest(pRef, pStr) >= 0)
{
// request server update
_ProtoMangleHttpRequest(pRef);
break;
}
// intentional fall-through in failure condition
case PROTOMANGLE_RESP_FAILURE:
case PROTOMANGLE_RESP_INVALID:
default:
pRef->eMangleState = ST_MNGL_FAIL;
break;
}
}
else if ((iRecvLen < 0) && (iRecvLen != PROTOHTTP_RECVWAIT))
{
pRef->eMangleState = ST_MNGL_FAIL;
}
}
// handle response to report
if (pRef->eMangleState == ST_MNGL_REPT)
{
// update HTTP request
ProtoHttpUpdate(pRef->pHttp);
// done?
if (ProtoHttpStatus(pRef->pHttp, 'done', NULL, 0) != 0)
{
pRef->eMangleState = (ProtoHttpStatus(pRef->pHttp, 'code', NULL, 0) == 200) ? ST_MNGL_DONE : ST_MNGL_FAIL;
}
}
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleComplete
\Description
Returns TRUE and fills in address and port if Demangler has finished operation successfully.
\Input *pRef - module state
\Input *pAddr - storage for address (result==TRUE)
\Input *pPort - storage for port (result==TRUE)
\Output
int32_t - TRUE if complete, else FALSE, or <0 to indicate an error
\Version 1.0 04/09/03 (JLB) First Version
*/
/*************************************************************************************************F*/
int32_t ProtoMangleComplete(ProtoMangleRefT *pRef, int32_t *pAddr, int32_t *pPort)
{
if (pRef->eMangleState == ST_MNGL_DONE)
{
// close probe socket
_ProtoMangleSockClose(pRef, TRUE);
// return address and port to caller
if ((pAddr != NULL) && (pPort != NULL))
{
*pAddr = pRef->iPeerAddr;
*pPort = pRef->iPeerPort;
}
// reset to IDLE state
pRef->eMangleState = ST_MNGL_IDLE;
return(TRUE);
}
else if ((pRef->eMangleState == ST_MNGL_WAIT) || (pRef->eMangleState == ST_MNGL_REPT))
{
return(FALSE);
}
else
{
pRef->eMangleState = ST_MNGL_IDLE;
return(-1);
}
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleReport
\Description
Post result report to server.
\Input *pRef - mangle ref
\Input eStatus - connection status
\Input iLatency - peer latency (optional, -1 to not send)
\Output
int32_t - zero=success, negative=failure
\Notes
Report is of the form:
POST http://demangler.ea.com:3658/connectionStatus
Cookie: sessionID=6ba7b810-9dad-11d1-80b4-00c04fd430c8
myIP=10.14.221.1&myPort=3250&version=1.0&status=connected&gameFeatureID=madden-ps2-2004&latency=80
\Version 1.0 07/18/03 (JLB) First Version
*/
/*************************************************************************************************F*/
int32_t ProtoMangleReport(ProtoMangleRefT *pRef, ProtoMangleStatusE eStatus, int32_t iLatency)
{
char strData[128 + (3 * DIRTYCERT_SERVICENAME_SIZE)], strAddrText[20], strServiceName[DIRTYCERT_SERVICENAME_SIZE];
int32_t iFormatted = 0;
// make sure status is valid
if (eStatus > PROTOMANGLE_STATUS_FAILED)
{
return(-1);
}
if (DirtyCertStatus('snam', strServiceName, DIRTYCERT_SERVICENAME_SIZE) < 0)
{
ds_snzprintf(strServiceName, sizeof(strServiceName), "invalid");
}
// format response to demangler server
iFormatted = ds_snzprintf(strData, sizeof(strData), "myIP=%s&myPort=%d&version=1.0&status=%s&gameFeatureID=%s\n",
SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), pRef->iPeerPort,
_ProtoMangle_pReportTable[eStatus], pRef->strGameID);
// add service name
ProtoHttpUrlEncodeStrParm(strData + iFormatted, sizeof(strData) - iFormatted, "&gameID=", strServiceName);
// add latency info, if included
if (iLatency >= 0)
{
char strData2[32];
ds_snzprintf(strData2, sizeof(strData2), "&latency=%d", iLatency);
ds_strnzcat(strData, strData2, sizeof(strData));
}
// post result to demangler server
_ProtoMangleHttpPost(pRef, "connectionStatus", pRef->strCookie, strData);
// set state & return success
pRef->eMangleState = ST_MNGL_REPT;
return(0);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleControl
\Description
ProtoMangle control function.
\Input *pRef - mangle ref
\Input iControl - control selector
\Input iValue - selector specific
\Input iValue2 - selector specific
\Input *pValue - selector specific
\Version 05/12/05 (JLB)
*/
/*************************************************************************************************F*/
int32_t ProtoMangleControl(ProtoMangleRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue)
{
if (iControl == 'abrt')
{
// close probe socket
_ProtoMangleSockClose(pRef, TRUE);
// reset to IDLE state
pRef->eMangleState = ST_MNGL_IDLE;
return(0);
}
if (iControl == 'time')
{
ProtoHttpControl(pRef->pHttp, 'time', iValue, 0, NULL);
return(0);
}
return(-1);
}
/*F*************************************************************************************************/
/*!
\Function ProtoMangleStatus
\Description
Return module status based on input selector.
\Input *pRef - mangle ref
\Input iSelect - status selector
\Input *pBuf - [out] storage for output data
\Input iBufSize - size of output buffer
\Version 1.0 01/13/05 (JLB)
*/
/*************************************************************************************************F*/
int32_t ProtoMangleStatus(ProtoMangleRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize)
{
if (iSelect == 'idle')
{
return(pRef->eMangleState == ST_MNGL_IDLE);
}
if (iSelect == 'time')
{
return(ProtoHttpStatus(pRef->pHttp, 'time', NULL, 0));
}
// unhandled
return(-1);
}