diff --git a/config.h b/config.h index 54e93b5..ec6d0f4 100644 --- a/config.h +++ b/config.h @@ -34,6 +34,8 @@ private: ~SectionIterator(); bool hasNext(); char* next(); + void add(char* part) {} + void remove() {} void reset(); private: diff --git a/gamelist.cpp b/gamelist.cpp index 6926b54..8231873 100644 --- a/gamelist.cpp +++ b/gamelist.cpp @@ -26,7 +26,6 @@ void GameList::cleanup() long GameList::getLastUpdate() { - lastUpdate++; return lastUpdate; } @@ -35,11 +34,12 @@ Iterator* GameList::createIterator() return glist.createIterator(); } -void GameList::addGame(int gameid, NetPkt* pkt, int port2) +void GameList::addGame(int gameid, NetPkt* pkt, int port2, bool swap) { char buf[64]; pkt->show(buf, sizeof(buf)); - LogSystem::log(LOG_NOTICE, "Adding Game %d: %s %d", gameid, buf, port2); + LogSystem::log(LOG_NOTICE, "Adding Game %d: %s", gameid, buf); glist.add(new GameEntry()); + lastUpdate++; } diff --git a/gamelist.h b/gamelist.h index d071c5c..5f0c095 100644 --- a/gamelist.h +++ b/gamelist.h @@ -13,7 +13,7 @@ public: ~GameList(); void cleanup(); - void addGame(int gameid, NetPkt* pkt, int port2 = 0); + void addGame(int gameid, NetPkt* pkt, int port2 = 0, bool swap = false); long getLastUpdate(); Iterator* createIterator(); diff --git a/gameparser.cpp b/gameparser.cpp index 2f57cd5..a5c3db9 100644 --- a/gameparser.cpp +++ b/gameparser.cpp @@ -9,21 +9,35 @@ GameParser::GameParser(RecvQueue& rxQueue, ModuleList& modList, GameList& gameLi int GameParser::execute(void* arg) { + NetPkt* pkt; + char buf[64], *p; + int ret; + while (1) { - NetPkt* pkt = rxQueue.getPkt(); - int ret = modList.parse(pkt, &gameList); + pkt = rxQueue.getPkt(); + ret = modList.parse(pkt, &gameList); switch (ret) { - case PARSE_REJECT: - char buf[64]; + case PARSE_ACCEPT_FAKE: pkt->show(buf, sizeof(buf)); - LogSystem::log(LOG_NOTICE, "unknown Packet: %s", buf); + LogSystem::log(LOG_NOTICE, "not supported Game: %s", buf); case PARSE_ACCEPT: delete pkt; case PARSE_ACCEPT_FREED: break; + + default: + case PARSE_REJECT: + pkt->show(buf, sizeof(buf)); + LogSystem::log(LOG_NOTICE, "unknown Packet: %s", buf); + + p = pkt->showfull(); + LogSystem::log(LOG_DEBUG, "%s", p); + delete p; + delete pkt; + break; } } return 0; diff --git a/hlswmaster.cpp b/hlswmaster.cpp index df4cd48..b9df7b1 100644 --- a/hlswmaster.cpp +++ b/hlswmaster.cpp @@ -91,9 +91,9 @@ int main(int argc, char *argv[]) HlswServer server(conf, gameList); // modList.reg(new ModHalfLife()); - modList.reg(new ModQ3Engine()); +// modList.reg(new ModQ3Engine()); // modList.reg(new ModD3Engine()); -// modList.reg(new ModGameSpy1()); + modList.reg(new ModGameSpy1()); // modList.reg(new ModGameSpy2()); server.start(); diff --git a/hlswserver.cpp b/hlswserver.cpp index 969a2be..56379e0 100644 --- a/hlswserver.cpp +++ b/hlswserver.cpp @@ -9,11 +9,8 @@ #include "logging.h" #include "hlswserver.h" -#define HLSW_MASTER_PORT 7140 static const char hlsw_header[] = "\xFF\xFF\xFF\xFFHLSWLANSEARCH"; -#define DEFAULT_REBUILD_INTERVAL 5 - struct hlsw_entry { uint16_t gameid; uint32_t ip; @@ -21,11 +18,15 @@ struct hlsw_entry { uint16_t port2; } __attribute__ ((packed)); +#define HLSW_MASTER_PORT 7140 +#define HLSW_MAX_PACKET_SIZE (sizeof(hlsw_header) + 140 * sizeof(struct hlsw_entry)) +#define DEFAULT_REBUILD_INTERVAL 5 + HlswServer::HlswPacket::HlswPacket(HlswPacket* next) -: NetPkt(1418), next(next) +: NetPkt(HLSW_MAX_PACKET_SIZE), next(next) { memcpy((void*)data, hlsw_header, sizeof(hlsw_header)); - used = sizeof(hlsw_header); + size = sizeof(hlsw_header); } HlswServer::HlswPacket::~HlswPacket() @@ -36,7 +37,7 @@ HlswServer::HlswPacket::~HlswPacket() bool HlswServer::HlswPacket::addGame(GameEntry* ge) { - if (used >= 1418) + if (size >= HLSW_MAX_PACKET_SIZE) return false; struct hlsw_entry tmp; @@ -45,8 +46,8 @@ bool HlswServer::HlswPacket::addGame(GameEntry* ge) tmp.port1 = ge->port1; tmp.port2 = ge->port2; - memcpy((void*)(data + used), &tmp, sizeof(tmp)); - used += sizeof(tmp); + memcpy((void*)(data + size), &tmp, sizeof(tmp)); + size += sizeof(tmp); return true; } @@ -99,7 +100,6 @@ int HlswServer::execute(void* arg) return 0; } - void HlswServer::rebuild(GameList& glist) { if (glist.getLastUpdate() > lastUpdate) { HlswPacket* newlist = new HlswPacket(NULL); diff --git a/list.h b/list.h index 0d2d6ae..6f27771 100644 --- a/list.h +++ b/list.h @@ -3,183 +3,145 @@ #include "mutex.h" -template -class Iterator { +class IteratorBase { public: - virtual ~Iterator() {} + virtual ~IteratorBase() {} virtual bool hasNext() =0; - virtual T* next() =0; + virtual void* next() =0; + virtual void add(void* part) {}; // TODO: why not =0; + virtual void remove() =0; virtual void reset() =0; protected: - Iterator() {} - Iterator(const Iterator& it); - Iterator& operator=(const Iterator& it); + IteratorBase() {} + IteratorBase(const IteratorBase& it); + IteratorBase& operator=(const IteratorBase& it); }; +class ListBase { +public: + ListBase(); + ~ListBase(); + + void add(const void* part); + void addTail(const void* part); + void* get(); + void* getTail(); + bool isEmpty() const; + + IteratorBase* createIterator(); + +protected: + ListBase(const ListBase& l); + ListBase& operator=(const ListBase& l); + + class ListEntryBase { + public: + ListEntryBase(const void* part = 0); + ~ListEntryBase(); + + void add(ListEntryBase* prev, ListEntryBase* next); + void del(); + bool isEmpty() const; + + void* getPart() const; + ListEntryBase* getPrev() const; + ListEntryBase* getNext() const; + + protected: + ListEntryBase(const ListEntryBase& l); + ListEntryBase& operator=(const ListEntryBase& l); + + private: + ListEntryBase* prev; + ListEntryBase* next; + const void* part; + }; + + class ListIteratorBase : public IteratorBase { + public: + ListIteratorBase(ListBase* list); + ~ListIteratorBase(); + + bool hasNext(); + void* next(); + void add(void* part); + void remove(); + void reset(); + + private: + ListBase* list; + ListEntryBase* pos; + }; + +private: + ListEntryBase head; + Mutex mutex; +}; + + +template +class Iterator : public IteratorBase { +public: + virtual T* next() =0; + virtual void add(T* part) =0; +}; template class NullIterator : public Iterator { public: - NullIterator() {} - ~NullIterator() {} - bool hasNext() { return false; } - T* next() { return 0; } - void reset() {} + NullIterator() {} + ~NullIterator() {} + + bool hasNext() { return false; } + T* next() { return NULL; } + void add(T* part) {} + void remove() {} + void reset() {} }; - template -class List { +class List : private ListBase { public: List() {} + ~List() {} - ~List() - { - AutoMutex am(mutex); - while(!head.isEmpty()) - head.getNext()->del(); - } + void add(const T* part) { ListBase::add(part); } + void addTail(const T* part) { ListBase::addTail(part); } + T* get() { return (T*)ListBase::get(); } + T* getTail() { return (T*)ListBase::getTail(); } + bool isEmpty() const { return ListBase::isEmpty(); } - void add(const T* part) - { - ListEntry* entry = new ListEntry(part); - - AutoMutex am(mutex); - entry->add(&head, head.getNext()); - } - - void addTail(const T* part) - { - ListEntry* entry = new ListEntry(part); - - AutoMutex am(mutex); - entry->add(head.getPrev(), &head); - } - - T* get() - { - AutoMutex am(mutex); - ListEntry* entry = head.getNext(); - T* retval = (T*)entry->getPart(); - - if (!head.isEmpty()) { - entry->del(); - delete entry; - } - return retval; - } - - T* getTail() - { - AutoMutex am(mutex); - ListEntry* entry = head.getPrev(); - T* retval = (T*)entry->getPart(); - - if (!head.isEmpty()) { - entry->del(); - delete entry; - } - return retval; - } - - bool isEmpty() const - { - AutoMutex am((Mutex&)mutex); - return head.isEmpty(); - } - - Iterator* createIterator() const - { - return new ListIterator(this); - } - -protected: - List(const List& l); - List& operator=(const List& l); + Iterator* createIterator() const { return new ListIteratorRO(this); } + Iterator* createIteratorRW() { return new ListIteratorRW(this); } private: template - class ListEntry { + class ListIteratorRO : public Iterator, private ListIteratorBase { public: - ListEntry(const TT* part = 0) - : prev(this), next(this), part(part) - { - } + ListIteratorRO(const List* list) : ListIteratorBase((ListBase*)list) {} + ~ListIteratorRO() {} - void add(ListEntry* prev_, ListEntry* next_) - { - this->next = next_; - this->prev = prev_; - next_->prev = this; - prev_->next = this; - } - - void del() - { - next->prev = prev; - prev->next = next; - next = prev = this; - } - - bool isEmpty() const - { - return (this->prev == this) && (this->next == this); - } - - const TT* getPart() const { return part; } - ListEntry* getPrev() const { return prev; } - ListEntry* getNext() const { return next; } - - private: - ListEntry* prev; - ListEntry* next; - const TT* part; + bool hasNext() { return ListIteratorBase::hasNext(); } + TT* next() { return (TT*)ListIteratorBase::next(); } + void add(TT* part) {} + void remove() {} + void reset() { ListIteratorBase::reset(); } }; - ListEntry head; - Mutex mutex; - template - class ListIterator : public Iterator { + class ListIteratorRW : public Iterator, private ListIteratorBase { public: - ListIterator(const List* list) - : list(list) - { - ((List*)list)->mutex.lock(); - reset(); - } + ListIteratorRW(List* list) : ListIteratorBase(list) {} + ~ListIteratorRW() {} - ~ListIterator() - { - ((List*)list)->mutex.unlock(); - } - - bool hasNext() - { - return (pos->getNext() != &list->head); - } - - TT* next() - { - pos = pos->getNext(); - return (TT*)pos->getPart(); - } - - void reset() - { - pos = &list->head; - } - - protected: - ListIterator(const ListIterator& lit); - ListIterator& operator=(const ListIterator& lit); - - private: - const List* list; - const ListEntry* pos; + bool hasNext() { return ListIteratorBase::hasNext(); } + TT* next() { return (TT*)ListIteratorBase::next(); } + void add(TT* part) { ListIteratorBase::add(part); } + void remove() { ListIteratorBase::remove(); } + void reset() { ListIteratorBase::reset(); } }; + }; - #endif //_LIST_H_ diff --git a/logging.cpp b/logging.cpp index 70b46e8..689dc0a 100644 --- a/logging.cpp +++ b/logging.cpp @@ -7,8 +7,8 @@ #include "logging.h" -#define BUFLEN 1024 - +/* we need this for packet dumps! */ +#define BUFLEN 8192 LogSystem::LogSystem() : output(0), priority(0) { diff --git a/mod_d3engine.cpp b/mod_d3engine.cpp index f458c86..7138af7 100644 --- a/mod_d3engine.cpp +++ b/mod_d3engine.cpp @@ -25,7 +25,7 @@ int ModD3Engine::parse(NetPkt* pkt, GameList* glist) if (!gameid) return PARSE_REJECT; - if (pkt->compare(0, replyhead, strlen(replyhead))) + if (!pkt->compare(0, replyhead, strlen(replyhead))) return PARSE_REJECT; glist->addGame(gameid, pkt); diff --git a/mod_gamespy1.cpp b/mod_gamespy1.cpp index f6a1fe4..6cb9ddb 100644 --- a/mod_gamespy1.cpp +++ b/mod_gamespy1.cpp @@ -1,24 +1,267 @@ #include +#include "logging.h" #include "modhelper.h" #include "mod_gamespy1.h" static struct game_ports port_arr[] = { - { 7777, 7788, ID_UT }, // ut(5), ut2k3(14), rune(18), ut2k4(33), aao(15) + { 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) + { 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() +{ + // TODO: cleanup event +} + +ModGameSpy1::~ModGameSpy1() +{ + while (!list.isEmpty()) { + MultiPart* mp = list.get(); + delete mp->pkt; + delete mp; + } +} void ModGameSpy1::scan(MultiSock* msock) { ModHelper::send(msock, port_arr, scanmsg, strlen(scanmsg)); } -int ModGameSpy1::parse(NetPkt* pkt, GameList* slist) +int ModGameSpy1::parse(NetPkt* pkt, GameList* glist) { - return 0; + 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); + + /* single final packet -> shortcut */ + if (final && (subid == 1)) { + LogSystem::log(LOG_DEBUG, "single final"); + pkt2 = pkt; + + /* non-final multi-part */ + } else if (!final) { + LogSystem::log(LOG_DEBUG, "non final"); + list.addTail(new MultiPart(pkt, queryid, subid)); + return PARSE_ACCEPT_FREED; + + /* final multipart */ + } else { + LogSystem::log(LOG_DEBUG, "multi final"); + pkt2 = merge(pkt, queryid, subid); + if (pkt2 == NULL) { + LogSystem::log(LOG_NOTICE, "GameSpy1: unable to merge"); + return PARSE_ACCEPT_FREED; + } + } + + retval = parse_real(pkt2, glist, gameid); + + if (pkt2 != pkt) + delete pkt2; + + return retval; +} + +ModGameSpy1::MultiPart::MultiPart(NetPkt* pkt, int queryid, int subid) +: pkt(pkt), queryid(queryid), subid(subid) +{ + timeout = time(NULL) + 5; +} + +NetPkt* ModGameSpy1::merge(NetPkt* pkt, int queryid, int subid) +{ + Iterator* it = list.createIteratorRW(); + + 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->append(mp->pkt); + searchid++; + } + } + + it->reset(); + } while (found && (searchid < subid)); + + delete it; + + tmppkt->append(pkt); + delete pkt; + + if (!found) { + delete tmppkt; + return NULL; + } + + return tmppkt; +} + +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, true); + + } else { + glist->addGame(gameid, pkt); + } + return PARSE_ACCEPT; } diff --git a/mod_gamespy1.h b/mod_gamespy1.h index f0a21b5..5c62c07 100644 --- a/mod_gamespy1.h +++ b/mod_gamespy1.h @@ -1,17 +1,36 @@ #ifndef _MODGAMESPY1_H_ #define _MODGAMESPY1_H_ +#include "list.h" #include "module.h" class ModGameSpy1 : public Module { public: - ModGameSpy1() {} - ~ModGameSpy1() {} + ModGameSpy1(); + ~ModGameSpy1(); void scan(MultiSock* msock); int parse(NetPkt* pkt, GameList* glist); const char* getName() { return "GameSpy 1 Protocol"; } + +private: + class MultiPart { + public: + MultiPart(NetPkt* pkt, int queryid, int subid); + ~MultiPart() {} + + NetPkt* pkt; + int queryid; + int subid; + + long timeout; + }; + + NetPkt* merge(NetPkt* pkt, int queryid, int subid); + int parse_real(NetPkt* pkt, GameList* glist, int gameid); + + List list; }; #endif // _MODGAMESPY1_H_ diff --git a/mod_gamespy2.cpp b/mod_gamespy2.cpp index 293dd37..cc180a1 100644 --- a/mod_gamespy2.cpp +++ b/mod_gamespy2.cpp @@ -1,25 +1,88 @@ #include +#include "logging.h" #include "modhelper.h" #include "mod_gamespy2.h" static struct game_ports port_arr[] = { - { 7777, 7788, ID_UT }, // ut(5), ut2k3(14), rune(18), ut2k4(33), aao(15) - { 22000, 22010, ID_BF1942 }, // bf1942(16) - { 23000, 23010, ID_BFV }, // bfv(35) - { 26001, 26011, ID_IGI2 }, // igi2(19) - { 27888, 27888, ID_AVP2 }, // avp2(17) - { 0,0,0 } + { 2302, 2302, ID_HALO }, + { 3455, 3455, ID_PK }, + { 10481, 10482, ID_SWAT4 }, + { 29900, 29910, ID_BF2 }, + { 0, 0, 0 } }; static const char scanmsg[] = { 0xFE, 0xFD, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0x00, 0x00 }; static const char replyhead[] = { 0x00, 0xDE, 0xAD, 0xBE, 0xEF }; +static const char search_hostport[] = "hostport"; +static const char search_gamename[] = "gamename"; + +static const char reply_bf2[] = "battlefield2"; + void ModGameSpy2::scan(MultiSock* msock) { ModHelper::send(msock, port_arr, scanmsg, sizeof(scanmsg)); } -int ModGameSpy2::parse(NetPkt* pkt, GameList* slist) +int ModGameSpy2::parse(NetPkt* pkt, GameList* glist) { - return 0; + int gameid, pos1, pos2, port; + + gameid = ModHelper::checkPorts(pkt, port_arr); + if (!gameid) + return PARSE_REJECT; + + if (!pkt->compare(0, replyhead, sizeof(replyhead))) + return PARSE_REJECT; + + pos1 = pkt->find(0, search_gamename, strlen(search_gamename)); + pos1 += strlen(search_gamename) +1; + + /* hostport angabe suchen */ + pos2 = pkt->find(0, search_hostport, strlen(search_hostport)); + if (pos2 != -1) { + pos2 += strlen(search_hostport) +1; + pkt->parse_int(pos2, &port); + } + + switch (gameid) { + case ID_HALO: + case ID_PK: + break; + + case ID_SWAT4: + if (pos2 == -1) + return PARSE_REJECT; + break; + + case ID_BF2:/* battlefield 2 */ + // TODO: pos2 check noetig? + if (pos2 == -1) { + LogSystem::log(LOG_DEBUG, "no hostport found"); + return PARSE_REJECT; + } + + if (pkt->find(pos1, reply_bf2, strlen(reply_bf2)) == -1) { + LogSystem::log(LOG_DEBUG, "no gamename found"); + return PARSE_REJECT; + } + break; + + default: + return PARSE_REJECT; + break; + } + + /* + * wenn ein hostport angegeben wurde, und das nicht der src port ist + * beide ports in die serverliste uebernehmen + */ + if ((pos2 != -1) && (port != pkt->getPort())) { + glist->addGame(gameid, pkt, port, true); + + } else { + glist->addGame(gameid, pkt); + } + + return PARSE_ACCEPT; } diff --git a/mod_halflife.cpp b/mod_halflife.cpp index 32b802d..026fd59 100644 --- a/mod_halflife.cpp +++ b/mod_halflife.cpp @@ -11,6 +11,9 @@ static const char scanmsg1[] = "\xff\xff\xff\xff""details"; static const char scanmsg2[] = "\xff\xff\xff\xff\x54"; static const char scanmsg3[] = "\xff\xff\xff\xff""TSource Engine Query"; +static const char reply1[] = "m\x00"; +static const char reply2[] = "I\x07"; + void ModHalfLife::scan(MultiSock* msock) { ModHelper::send(msock, port_arr, scanmsg1, strlen(scanmsg1)); @@ -18,7 +21,42 @@ void ModHalfLife::scan(MultiSock* msock) ModHelper::send(msock, port_arr, scanmsg3, strlen(scanmsg3)); } -int ModHalfLife::parse(NetPkt* pkt, GameList* slist) +int ModHalfLife::parse(NetPkt* pkt, GameList* glist) { - return 0; + struct in_addr tmp; + int port, count, pos; + + if (ModHelper::checkPorts(pkt, port_arr) == 0) + return PARSE_REJECT; + + // check 0xFF 0xFF 0xFF 0xFF + if (!pkt->compare(0, scanmsg1, 4)) + return PARSE_REJECT; + + /* check for short answer without ip/port */ + if (pkt->compare(4, reply1, 2)) { + glist->addGame(ID_HL, pkt); + return PARSE_ACCEPT; + } + + /* second query?! */ + if (pkt->compare(4, reply2, 2)) { + glist->addGame(ID_HL2, pkt); + return PARSE_ACCEPT; + } + + /* parse server IP */ + pos = 5; + if ((count = pkt->parse_ip(pos, &tmp)) == 0) + return PARSE_REJECT; + + /* parse server port */ + pos += count +1; + if ((count = pkt->parse_int(pos, &port)) == 0) + return PARSE_REJECT; + + // TODO: check server IP? + //glist->add(ID_HL, &tmp, port, 0); + glist->addGame(ID_HL, pkt); + return PARSE_ACCEPT; } diff --git a/mod_q3engine.cpp b/mod_q3engine.cpp index 8caa8c3..0d1ca1d 100644 --- a/mod_q3engine.cpp +++ b/mod_q3engine.cpp @@ -3,10 +3,10 @@ #include "mod_q3engine.h" static struct game_ports port_arr[] = { - { 27960, 27960, ID_Q3A }, // q3(6), ef(7), et25), rtcw(8) -// { 28070, 28070, ID_JK2 }, // jk2(12) -// { 28960, 28963, ID_COD }, // cod(31), cod:uo(42), cod2(48) -// { 29070, 29070, ID_JK3 }, // jk3(27) + { 27960, 27969, ID_Q3A }, // q3(6), ef(7), et25), rtcw(8) + { 28070, 28070, ID_JK2 }, // jk2(12) + { 28960, 28963, ID_COD }, // cod(31), cod:uo(42), cod2(48) + { 29070, 29070, ID_JK3 }, // jk3(27) { 0,0,0 } }; diff --git a/multisock.cpp b/multisock.cpp index cf1f856..2f3f029 100644 --- a/multisock.cpp +++ b/multisock.cpp @@ -108,6 +108,6 @@ int MultiSock::sendto(NetPkt* pkt, struct sockaddr_in* dst) delete it; - usleep(10000); + usleep(1000); return 0; } diff --git a/netpkt.cpp b/netpkt.cpp index 95ad3e8..f4d4ffc 100644 --- a/netpkt.cpp +++ b/netpkt.cpp @@ -8,25 +8,26 @@ #include #include #include +#include #include "logging.h" #include "module.h" #include "netpkt.h" NetPkt::NetPkt(const char* data, int size) -: data(data), used(size), size(0) +: data(data), size(size), alloc(0) { } -NetPkt::NetPkt(int size) -: used(0), size(size) +NetPkt::NetPkt(int alloc) +: size(0), alloc(alloc) { - data = new char[size]; + data = new char[alloc]; } NetPkt::~NetPkt() { - if (size > 0) + if (alloc > 0) delete data; } @@ -35,27 +36,23 @@ int NetPkt::show(char* buf, int size) return snprintf(buf, size, "(%s:%d) (%d/%d) bytes", inet_ntoa(this->addr.sin_addr), ntohs(this->addr.sin_port), - this->used, this->size); + this->size, this->alloc); } -bool NetPkt::compare(int offset, const char* buf, int len) +bool NetPkt::compare(unsigned int offset, const char* buf, unsigned int len) { - if (offset >= this->used) + if (offset + len >= this->size) return false; - /* nicht ueber das paket hinaus vergleichen */ - if (offset + len >= this->used) - len = this->used - offset; - return (memcmp(this->data + offset, buf, len) == 0); } -int NetPkt::find(int offset, const char *buf, int len) +int NetPkt::find(unsigned int offset, const char *buf, unsigned int len) { - if (offset >= this->used) + if (offset >= this->size) return -1; - void* found = memmem(this->data + offset, this->used, buf, len); + void* found = memmem(this->data + offset, this->size, buf, len); return (found == NULL) ? -1 : ((char*)found - this->data); } @@ -69,3 +66,117 @@ struct sockaddr_in * NetPkt::getAddress() { return &addr; } + +int NetPkt::parse_int(unsigned int offset, int *val) +{ + const char *max = this->data + this->size; + const char *c = this->data + offset; + + /* untere grenze abtesten */ + if (this->data > c || c > max) + return -1; + + *val = 0; + + /* ziffern einlesen */ + while (isdigit(*c) && c < max) + *val = (*val * 10) + (*c++ - 0x30); + + return (c - (this->data + offset)); +} + +int NetPkt::parse_ip(unsigned int offset, struct in_addr *ip) +{ + int i, tmp, count, pos = offset; + ip->s_addr = 0; + + for (i = 0; i < 4; i++) { + count = this->parse_int(pos, &tmp); + pos += count; + if (count == 0 || tmp < 0 || tmp > 255) + return 0; + + ip->s_addr = ip->s_addr>>8 | tmp<<24; + + if (i != 3 && this->data[pos++] != '.') + return 0; + } + return pos - offset; +} + +int NetPkt::getPort() +{ + return ntohs(addr.sin_port); +} + +char* NetPkt::showfull() +{ + unsigned int pos = 0, i = 0, j; + char *buf = new char[this->size * 4 + 64]; + + while (pos < this->size) { + i += sprintf(buf + i, "%04X: ", pos); + for (j = 0; j < 16; j++) { + if (pos + j < this->size) + i += sprintf(buf + i, "%02X", (unsigned char)this->data[pos + j]); + else + i += sprintf(buf + i, " "); + + if (j % 2) + buf[i++] = ' '; + } + + for (j = 0; j < 16; j++) { + if (pos + j < this->size) { + unsigned char val = this->data[pos + j]; + if (val >= 0x20 && val < 0x80) + buf[i++] = val; + else + buf[i++] = '.'; + } else { + buf[i++] = ' '; + } + } + + pos += 16; + buf[i++] = '\r'; + buf[i++] = '\n'; + } + buf[i] = 0; + return buf; +} + +int NetPkt::getSize() +{ + return size; +} + +bool NetPkt::sameAddress(NetPkt* pkt) +{ + return (this->addr.sin_addr.s_addr == pkt->addr.sin_addr.s_addr) && + (this->addr.sin_port == pkt->addr.sin_port); +} + +void NetPkt::append(NetPkt* pkt) +{ + char buf1[64], buf2[64]; + this->show(buf1, sizeof(buf1)); + pkt->show(buf2, sizeof(buf2)); + LogSystem::log(LOG_DEBUG, "append (%s) + (%s)", buf1, buf2); + + unsigned int new_alloc = size + pkt->size; + + char* new_data = new char[new_alloc]; + memcpy(new_data, data, size); + + if (alloc) + delete data; + + data = new_data; + alloc = new_alloc; + + memcpy((void*)(data + size), pkt->data, pkt->size); + size += pkt->size; + + delete pkt; +} diff --git a/netpkt.h b/netpkt.h index 6f8b69e..98e6948 100644 --- a/netpkt.h +++ b/netpkt.h @@ -6,6 +6,8 @@ #define PARSE_ACCEPT 1 #define PARSE_ACCEPT_FREED 2 +#define PARSE_ACCEPT_FAKE 3 +#define PARSE_ACCEPT_QUIRK PARSE_ACCEPT #define PARSE_REJECT 4 /* avoid cyclic deps */ @@ -15,15 +17,26 @@ class NetPkt { friend class Socket; public: NetPkt(const char* data, int size); - NetPkt(int size); + NetPkt(int alloc); ~NetPkt(); int show(char* buf, int size); - bool compare(int offset, const char* buf, int len); - int find(int offset, const char *buf, int len); + char* showfull(); + + bool compare(unsigned int offset, const char* buf, unsigned int len); + int find(unsigned int offset, const char *buf, unsigned int len); + + int parse_int(unsigned int offset, int *val); + int parse_ip(unsigned int offset, struct in_addr *ip); void setAddress(struct sockaddr_in *addr); struct sockaddr_in * getAddress(); + bool sameAddress(NetPkt* pkt); + + void append(NetPkt* pkt); + + int getPort(); + int getSize(); protected: NetPkt(const NetPkt& x); @@ -31,10 +44,10 @@ protected: struct sockaddr_in addr; const char *data; - int used; + unsigned int size; private: - int size; + unsigned int alloc; }; #endif // _NETPKT_H_ diff --git a/socket.cpp b/socket.cpp index 150cc4b..72ca4e7 100644 --- a/socket.cpp +++ b/socket.cpp @@ -155,7 +155,7 @@ int Socket::sendto(NetPkt* pkt, struct sockaddr_in* dst) { if (!dst) dst = &pkt->addr; - if (::sendto(fd, pkt->data, pkt->used, 0, (struct sockaddr *)dst, sizeof(*dst)) < 0) + if (::sendto(fd, pkt->data, pkt->size, 0, (struct sockaddr *)dst, sizeof(*dst)) < 0) LogSystem::log(LOG_WARN, "Socket::sendto()"); return 0; } @@ -169,7 +169,7 @@ NetPkt* Socket::recv() { NetPkt* pkt = new NetPkt(this->getRecvSize()); socklen_t i = sizeof(pkt->addr); - pkt->used = ::recvfrom(fd, (void*)pkt->data, pkt->size, 0, (struct sockaddr *)&pkt->addr, &i); + pkt->size = ::recvfrom(fd, (void*)pkt->data, pkt->alloc, 0, (struct sockaddr *)&pkt->addr, &i); return pkt; } diff --git a/timerservice.cpp b/timerservice.cpp index 0935c25..d939233 100644 --- a/timerservice.cpp +++ b/timerservice.cpp @@ -2,6 +2,8 @@ #include "timerservice.h" +// TODO: how to deregister TimerEvents? + Timer::Timer(Event* event, unsigned int timeval) : next(0), event(event), timeval(timeval) {