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