hlswmaster-ng (now in c++)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

311 lines
8.2 KiB

/***************************************************************************
* Copyright (C) 04/2006 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <string.h>
#include "mod_gamespy1.h"
#include "modhelper.h"
#include "logging.h"
#define MODGS1_GC_TIMEOUT 5
static struct game_ports port_arr[] = {
{ 7777, 7788, ID_UT }, // ut, ut2k3, rune, ut2k4, aao, POSTAL2
{ 22000, 22010, ID_BF1942 }, // bf1942(16)
{ 23000, 23010, ID_BFV }, // bfv(35)
{ 26001, 26011, ID_IGI2 }, // igi2(19)
{ 27888, 27888, ID_AVP2 }, // avp2(17), FEAR
{ 0,0,0 }
};
static const char scanmsg[] = "\\status\\";
static const char search_queryid[] = "\\queryid\\";
static const char search_final[] = "\\final\\";
static const char search_hostport[] = "\\hostport\\";
static const char search_gamename[] = "\\gamename\\";
static const char search_gsgamename[] = "\\gsgamename\\";
static const char search_gameid[] = "\\game_id\\";
static const char reply_ut[] = "ut\\";
static const char reply_ut2k3[] = "ut2\\";
static const char reply_ut2k4[] = "ut2004\\";
static const char reply_bf1942[] = "bfield1942\\";
static const char reply_bfv1[] = "bfvietnam\\";
static const char reply_bfv2[] = "BFVIETNAM\\";
static const char reply_poe[] = "poe\\";
static const char reply_opk[] = "opk\\";
static const char reply_avp2[] = "avp2\\";
static const char reply_igi2[] = "projectigi2r\\";
static const char reply_aao[] = "armygame\\";
static const char reply_rune[] = "rune\\";
static const char reply_postal2[] = "postal2\\";
static const char reply_fear[] = "fear\\";
ModGameSpy1::ModGameSpy1()
{
TimerService::registerTimer(new Timer(new GcEvent(*this), MODGS1_GC_TIMEOUT));
}
ModGameSpy1::~ModGameSpy1()
{
while (!list.isEmpty())
delete list.get();
}
void ModGameSpy1::scan(MultiSock* msock)
{
ModHelper::send(msock, port_arr, scanmsg, strlen(scanmsg));
}
int ModGameSpy1::parse(NetPkt* pkt, GameList* glist)
{
NetPkt* pkt2;
int gameid, pos, offset, queryid, subid, retval;
bool final;
gameid = ModHelper::checkPorts(pkt, port_arr);
if (!gameid)
return PARSE_REJECT;
/* eat ut connection attemps */
if (gameid == 5 && pkt->getSize() <= 6)
return PARSE_ACCEPT_QUIRK;
offset = pkt->find(0, search_queryid, strlen(search_queryid));
if (offset == -1)
return PARSE_REJECT;
pos = offset + strlen(search_queryid);
offset = pkt->parse_int(pos, &queryid);
if (offset == 0)
return PARSE_REJECT;
pos += offset +1;
offset = pkt->parse_int(pos, &subid);
if (offset == 0)
return PARSE_REJECT;
offset = pkt->find(0, search_final, strlen(search_final));
final = (offset != -1);
char buf[64];
pkt->show(buf, sizeof(buf));
/* single final packet -> shortcut */
if (final && (subid == 1)) {
pkt2 = pkt;
/* non-final multi-part */
} else if (!final) {
LogSystem::log(LOG_DEBUG, "non final %s (%d/%d)", buf, queryid, subid);
list.add(new MultiPart(pkt, queryid, subid));
return PARSE_ACCEPT_FREED; // parser must not free pkt
/* final multipart */
} else {
LogSystem::log(LOG_DEBUG, "multi final %s (%d/%d)", buf, queryid, subid);
pkt2 = merge(pkt, queryid, subid);
if (pkt2 == NULL) {
LogSystem::log(LOG_NOTICE, "ModGameSpy1: failed to merge");
return PARSE_ACCEPT; // let parser free pkt
}
}
retval = parse_real(pkt2, glist, gameid);
if (pkt2 != pkt)
delete pkt2; // free merged packet
return retval; // ACCPET/REJECT -> let parser free pkt
}
ModGameSpy1::MultiPart::MultiPart(NetPkt* pkt, int queryid, int subid)
: pkt(pkt), queryid(queryid), subid(subid)
{
timeout = time(NULL) + MODGS1_GC_TIMEOUT;
}
ModGameSpy1::MultiPart::~MultiPart()
{
delete pkt;
}
NetPkt* ModGameSpy1::merge(NetPkt* pkt, int queryid, int subid)
{
Iterator<MultiPart>* it = list.createIterator();
NetPkt* tmppkt = new NetPkt(NULL, 0);
tmppkt->setAddress(pkt->getAddress());
bool found;
int searchid = 1;
do {
found = false;
while (it->hasNext()) {
MultiPart* mp = it->next();
if (!pkt->sameAddress(mp->pkt))
continue;
if (queryid != mp->queryid)
continue;
it->remove();
found = true;
if (searchid == mp->subid) {
tmppkt->merge(mp->pkt);
delete mp;
searchid++;
}
}
it->reset();
} while (found && (searchid < subid));
delete it;
tmppkt->merge(pkt);
if (!found) {
delete tmppkt;
return NULL;
}
return tmppkt;
}
void ModGameSpy1::gc()
{
Iterator<MultiPart>* it = list.createIterator();
long now = time(NULL);
char buf[64];
while (it->hasNext()) {
MultiPart* mp = it->next();
if (mp->timeout <= now) {
mp->pkt->show(buf, sizeof(buf));
LogSystem::log(LOG_NOTICE, "ModGameSpy1 gc removed: %s (%d/%d)",
buf, mp->queryid, mp->subid);
it->remove();
delete mp;
}
}
delete it;
}
int ModGameSpy1::parse_real(NetPkt* pkt, GameList* glist, int gameid)
{
int port, offset, pos1, pos2;
pos1 = pkt->find(0, search_gamename, strlen(search_gamename));
pos1 += strlen(search_gamename);
switch (gameid) {
case ID_UT:
if (pkt->compare(pos1, reply_ut, strlen(reply_ut)))
gameid = ID_UT;
else if (pkt->compare(pos1, reply_ut2k3, strlen(reply_ut2k4)))
gameid = ID_UT2K3;
else if (pkt->compare(pos1, reply_ut2k4, strlen(reply_ut2k4)))
gameid = ID_UT2K4;
else if (pkt->compare(pos1, reply_aao, strlen(reply_aao)))
gameid = ID_AAO;
else if (pkt->compare(pos1, reply_postal2, strlen(reply_postal2)))
return PARSE_ACCEPT_FAKE;
else
return PARSE_REJECT;
break;
case ID_BF1942:
case ID_BFV:
pos2 = pkt->find(0, search_gameid, strlen(search_gameid));
pos2 += strlen(search_gameid);
if (pkt->compare(pos1, reply_bf1942, strlen(reply_bf1942)))
gameid = ID_BF1942;
else if (pkt->compare(pos2, reply_bfv1, strlen(reply_bfv1)))
gameid = ID_BFV;
else if (pkt->compare(pos2, reply_bfv2, strlen(reply_bfv2)))
gameid = ID_BFV;
else if (pkt->compare(pos2, reply_poe, strlen(reply_poe)))
gameid = ID_BFV;
else if (pkt->compare(pos2, reply_opk, strlen(reply_opk)))
gameid = ID_BFV;
else
return PARSE_REJECT;
break;
case ID_AVP2:
pos2 = pkt->find(0, search_gsgamename, strlen(search_gsgamename));
pos2 += strlen(search_gsgamename);
if (pkt->compare(pos1, reply_avp2, strlen(reply_avp2)))
gameid = ID_AVP2;
else if (pkt->compare(pos2, reply_fear, strlen(reply_fear)))
return PARSE_ACCEPT_FAKE;
else
return PARSE_REJECT;
break;
case ID_RUNE:
if (pkt->compare(pos1, reply_rune, strlen(reply_rune)))
gameid = ID_RUNE;
else
return PARSE_REJECT;
break;
case ID_IGI2:
if (pkt->compare(pos1, reply_igi2, strlen(reply_igi2)))
gameid = ID_IGI2;
else
return PARSE_REJECT;
break;
default:
return PARSE_REJECT;
}
/* hostport angabe suchen */
offset = pkt->find(0, search_hostport, strlen(search_hostport));
if (offset != -1)
pkt->parse_int(offset + strlen(search_hostport), &port);
/*
* wenn ein hostport angegeben wurde, und das nicht der src port ist
* beide ports in die serverliste uebernehmen
*/
if ((offset != -1) && (port != pkt->getPort())) {
glist->addGame(gameid, pkt, port);
} else {
glist->addGame(gameid, pkt);
}
return PARSE_ACCEPT;
}