#include #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* 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* 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; }