From ba91546d887ca65b80b56e1428bbbad82d40c224 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Thu, 2 Feb 2006 16:55:44 +0100 Subject: [PATCH] Initial Commit --- Makefile | 22 ++++++ config.cpp | 197 +++++++++++++++++++++++++++++++++++++++++++++++ config.h | 47 +++++++++++ gamelist.h | 31 ++++++++ gameparser.cpp | 42 ++++++++++ gameparser.h | 39 ++++++++++ gamescanner.cpp | 50 ++++++++++++ gamescanner.h | 49 ++++++++++++ hlswmaster.conf | 16 ++++ hlswmaster.cpp | 106 +++++++++++++++++++++++++ hlswserver.cpp | 78 +++++++++++++++++++ hlswserver.h | 46 +++++++++++ list.h | 167 +++++++++++++++++++++++++++++++++++++++ logging.cpp | 99 ++++++++++++++++++++++++ logging.h | 63 +++++++++++++++ mod_example.h | 35 +++++++++ modulelist.cpp | 42 ++++++++++ modulelist.h | 40 ++++++++++ multisock.cpp | 187 ++++++++++++++++++++++++++++++++++++++++++++ multisock.h | 53 +++++++++++++ mutex.h | 35 +++++++++ netpkt.cpp | 55 +++++++++++++ netpkt.h | 32 ++++++++ recvqueue.h | 15 ++++ semaphore.h | 21 +++++ thread.cpp | 77 ++++++++++++++++++ thread.h | 30 ++++++++ timerservice.cpp | 71 +++++++++++++++++ timerservice.h | 54 +++++++++++++ 29 files changed, 1799 insertions(+) create mode 100644 Makefile create mode 100644 config.cpp create mode 100644 config.h create mode 100644 gamelist.h create mode 100644 gameparser.cpp create mode 100644 gameparser.h create mode 100644 gamescanner.cpp create mode 100644 gamescanner.h create mode 100644 hlswmaster.conf create mode 100644 hlswmaster.cpp create mode 100644 hlswserver.cpp create mode 100644 hlswserver.h create mode 100644 list.h create mode 100644 logging.cpp create mode 100644 logging.h create mode 100644 mod_example.h create mode 100644 modulelist.cpp create mode 100644 modulelist.h create mode 100644 multisock.cpp create mode 100644 multisock.h create mode 100644 mutex.h create mode 100644 netpkt.cpp create mode 100644 netpkt.h create mode 100644 recvqueue.h create mode 100644 semaphore.h create mode 100644 thread.cpp create mode 100644 thread.h create mode 100644 timerservice.cpp create mode 100644 timerservice.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb5cebc --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +CFLAGS := -O2 -pipe -Wall -Wunused -Wno-deprecated +LIBS := -lpthread + +SRC := $(wildcard *.cpp) +OBJS := $(SRC:.cpp=.o) +DEPS := $(SRC:.cpp=.d) + +all: hlswmaster + +hlswmaster: $(OBJS) + $(CXX) $(CFLAGS) $^ $(LIBS) -o $@ + +%.o: %.cpp + $(CXX) $(CFLAGS) -c $< -o $@ + +%.d: %.cpp + $(CXX) $(CFLAGS) -MM -c $< -o $@ + +clean: + rm -f hlswmaster *.d *.o *.log + +-include $(DEPS) diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..dd6db22 --- /dev/null +++ b/config.cpp @@ -0,0 +1,197 @@ +#include +#include +#include + +#include "config.h" +#include "logging.h" + +/* ------ */ + +Config::Tupel::Tupel(const char* name_, const char* value_) +{ + name = strdup(name_); + value = strdup(value_); +} + +Config::Tupel::~Tupel() +{ + free(name); + free(value); +} + +/* ------ */ + +Config::Section::Section(const char* name_) +{ + name = strdup(name_); +} + +Config::Section::~Section() +{ + while (!tupelList.isEmpty()) + delete tupelList.get(); + + free(name); +} + +bool Config::Section::addTupel(const char* name, const char* value) +{ + if (!name || !value) + return false; + + Tupel* t = new Tupel(name, value); + tupelList.addTail(t); + + return true; +} + +void Config::Section::show() const +{ + Iterator* it = tupelList.createIterator(); + LogSystem::log(LOG_DEBUG, "[%s]", name); + + while (it->hasNext()) { + Tupel* t = it->next(); + LogSystem::log(LOG_DEBUG, " %s = %s", t->name, t->value); + } + + delete it; +} + +char* Config::Section::getTupelValue(const char* name) const +{ + char* retval = 0; + Iterator* it = this->tupelList.createIterator(); + while (it->hasNext()) { + Tupel* t = it->next(); + if (!strcmp(t->name, name)) { + retval = t->value; + break; + } + } + + delete it; + return retval; +} + +/* ------ */ + +Config::~Config() +{ + while (!sectionList.isEmpty()) + delete sectionList.get(); +} + +Config::Section* Config::addSection(const char* name) +{ + Section* s = new Section(name); + sectionList.addTail(s); + return s; +} + +bool Config::parseFile(const char* name) +{ + Config::Section* section = 0; + FILE *fz; + int i = 0; + char *row, *tok, *tok2; + bool ret = true; + + if (!(row = (char*)malloc(1024))) { + LogSystem::log(LOG_CRIT, "config_parse(): malloc()"); + return false; + } + + if (!(fz = fopen(name, "r"))) { + LogSystem::log(LOG_ERROR, "config_parse(): can not open %s", name); + return false; + } + + while (fgets(row, 1024, fz)) { + i++; + + /* kommentar oder leere zeile */ + if (row[0] == '#' || row[0] <= ' ') { + continue; + + /* neue section */ + } else if (row[0] == '[') { + tok = strtok(row +1, " ]\n"); + section = addSection(tok); + if (!section) { + LogSystem::log(LOG_WARNING, "config_parse(): invalid section in row %d", i); + ret = false; + break; + } + continue; + + /* option, aber es gab noch keine section */ + } else if (!section) { + LogSystem::log(LOG_WARNING, "config_parse(): missing section in row %d", i); + ret = false; + break; + } + + /* option */ + if ((tok = strtok(row, " \n")) && (tok2 = strtok(NULL, " \n"))) { + if (!section->addTupel(tok, tok2)) + LogSystem::log(LOG_WARNING, "config_parse(): invalid row %d", i); + } + } + + fclose(fz); + free(row); + + return ret; +} + +void Config::show() const +{ + LogSystem::log(LOG_DEBUG, "Config Dump:"); + Iterator
* it = sectionList.createIterator(); + while (it->hasNext()) + it->next()->show(); + + delete it; +} + +char* Config::getParameter(const char* section, const char* option) const +{ + char* retval = 0; + + Iterator
* it = sectionList.createIterator(); + while (it->hasNext()) { + Section* s = it->next(); + if (!strcmp(s->name, section)) { + retval = s->getTupelValue(option); + break; + } + } + + delete it; + return retval; +} + +char* Config::getString(const char* section, const char* option, char* def) const +{ + char* retval = getParameter(section, option); + if (!retval) { + LogSystem::log(LOG_NOTICE, + "Config: [%s:%s] not found => using '%s'", + section, option, def); + return def; + } + return retval; +} + +int Config::getInteger(const char* section, const char* option, int def) const +{ + char* retval = getParameter(section, option); + if (!retval) { + LogSystem::log(LOG_NOTICE, + "Config: [%s:%s] not found => using '%d'", + section, option, def); + return def; + } + return atoi(retval); +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..65844c1 --- /dev/null +++ b/config.h @@ -0,0 +1,47 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include "list.h" + +class Config { +public: + ~Config(); + + bool parseFile(const char* name); + void show() const; + + char* getParameter(const char* section, const char* name) const; + char* getString(const char* section, const char* name, char* def) const; + int getInteger(const char* section, const char* name, int def) const; + +private: + class Tupel { + public: + Tupel(const char* name, const char* value); + ~Tupel(); + + char* name; + char* value; + }; + + class Section { + public: + Section(const char* name); + ~Section(); + + bool addTupel(const char* name, const char* option); + char* getTupelValue(const char* name) const; + void show() const; + + char* name; + + private: + List tupelList; + }; + + Section* addSection(const char* name); + + List
sectionList; +}; + +#endif // _CONFIG_H_ diff --git a/gamelist.h b/gamelist.h new file mode 100644 index 0000000..cc37a12 --- /dev/null +++ b/gamelist.h @@ -0,0 +1,31 @@ +#ifndef _GAMELIST_H_ +#define _GAMELIST_H_ + +class GameEntry { +public: + GameEntry(); + ~GameEntry(); + +protected: + GameEntry(const GameEntry& ge); + GameEntry& operator=(const GameEntry& ge); + +private: + struct in_addr addr; + int port1; + int port2; + int gameid; +}; + + +class GameList { +public: + virtual ~GameList() {}; + + virtual void cleanup() {}; + +protected: + GameList() {} +}; + +#endif // _GAMELIST_H_ diff --git a/gameparser.cpp b/gameparser.cpp new file mode 100644 index 0000000..e564ffc --- /dev/null +++ b/gameparser.cpp @@ -0,0 +1,42 @@ +#include "logging.h" +#include "netpkt.h" +#include "gameparser.h" + +#define DEFAULT_TIMEOUT 180 + +GameParser::GameParser(Config& conf, RecvQueue& rxQueue, ModuleList& modList) +: rxQueue(rxQueue), modList(modList) +{ + int interval = conf.getInteger("global", "game_timeout", DEFAULT_TIMEOUT); + TimerService::registerTimer(new Timer(new CleanupEvent(this), interval)); +} + +GameParser::~GameParser() +{ +} + +int GameParser::execute(void* arg) +{ + while (1) { + NetPkt* pkt = rxQueue.getPkt(); + int ret = modList.parse(pkt, this); + switch (ret) { + case PKT_REJECT: + char buf[64]; + pkt->show(buf, sizeof(buf)); + LogSystem::log(LOG_DEBUG, "unknown Packet: %s", buf); + + case PKT_ACCEPT: + delete pkt; + + case PKT_ACCEPT_FREED: + break; + } + } + return 0; +} + +void GameParser::cleanup() +{ + LogSystem::log(LOG_DEBUG, "GameParser::cleanup()"); +} diff --git a/gameparser.h b/gameparser.h new file mode 100644 index 0000000..b4d5532 --- /dev/null +++ b/gameparser.h @@ -0,0 +1,39 @@ +#ifndef _GAMEPARSER_H_ +#define _GAMEPARSER_H_ + +#include "gamelist.h" +#include "thread.h" +#include "config.h" +#include "recvqueue.h" +#include "modulelist.h" +#include "timerservice.h" + +class GameParser : public GameList, public Thread { +public: + GameParser(Config& conf, RecvQueue& rxQueue, ModuleList& modlist); + ~GameParser(); + + int execute(void* arg); + + void cleanup(); + +protected: + GameParser(const GameParser& rp); + GameParser& operator=(const GameParser& rp); + +private: + class CleanupEvent : public Command { + public: + CleanupEvent(GameList* sl) : sl(sl) {} + ~CleanupEvent() {} + void execute() { sl->cleanup(); } + + private: + GameList* sl; + }; + + RecvQueue& rxQueue; + ModuleList& modList; +}; + +#endif // _GAMEPARSER_H_ diff --git a/gamescanner.cpp b/gamescanner.cpp new file mode 100644 index 0000000..e45e322 --- /dev/null +++ b/gamescanner.cpp @@ -0,0 +1,50 @@ +#include "logging.h" +#include "timerservice.h" + +#include "gamescanner.h" + +#define DEFAULT_INTERVAL 30 + +GameScanner::GameScanner(Config& conf, ModuleList& modList) +: modList(modList) +{ + msock = new MultiSock(conf); + pktCount = new Semaphore(0); + + int interval = conf.getInteger("global", "scan_interval", DEFAULT_INTERVAL); + TimerService::registerTimer(new Timer(new ScanEvent(this), interval)); +} + +GameScanner::~GameScanner() +{ + while (!pktList.isEmpty()) + delete pktList.get(); + + delete pktCount; + delete msock; +} + +int GameScanner::execute(void* arg) +{ + while (1) { + int fd = msock->getRecvSocket(); + NetPkt* pkt = NetPkt::createFromSocket(fd); + + if (pkt != NULL) { + pktList.addTail(pkt); + pktCount->post(); + } + } + return 0; +} + +void GameScanner::scan() +{ + modList.scan(msock); +} + +NetPkt* GameScanner::getPkt() +{ + pktCount->wait(); + return pktList.get(); +} diff --git a/gamescanner.h b/gamescanner.h new file mode 100644 index 0000000..2978aa0 --- /dev/null +++ b/gamescanner.h @@ -0,0 +1,49 @@ +#ifndef _GAMESCANNER_H_ +#define _GAMESCANNER_H_ + +#include "recvqueue.h" +#include "thread.h" +#include "config.h" +#include "modulelist.h" + +#include "multisock.h" +#include "semaphore.h" +#include "list.h" +#include "netpkt.h" + +#include "timerservice.h" + +class GameScanner : public RecvQueue, public Thread { +public: + GameScanner(Config& conf, ModuleList& modList); + ~GameScanner(); + + void scan(); + + NetPkt* getPkt(); + int execute(void* arg); + +protected: + GameScanner(const GameScanner& hs); + GameScanner& operator=(const GameScanner& hs); + +private: + class ScanEvent : public Command { + public: + ScanEvent(GameScanner* gs) : gs(gs) {} + ~ScanEvent() {} + void execute() { gs->scan(); } + + private: + GameScanner* gs; + }; + + ModuleList& modList; + MultiSock* msock; + Semaphore* pktCount; + List pktList; +}; + + + +#endif // _SCANNER_H_ diff --git a/hlswmaster.conf b/hlswmaster.conf new file mode 100644 index 0000000..9eb90f1 --- /dev/null +++ b/hlswmaster.conf @@ -0,0 +1,16 @@ +[global] +# broadcast scan source port (udp) +scan_port 7130 + +# broadcast scan every X seconds +scan_interval 10 + +# server timeout after X seconds +game_timeout 30 + +# master answers with this source IP +master_ip 0.0.0.0 + +# logging +logfile hlswmaster.log +logprio 7 diff --git a/hlswmaster.cpp b/hlswmaster.cpp new file mode 100644 index 0000000..878a3b5 --- /dev/null +++ b/hlswmaster.cpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include "logging.h" +#include "config.h" +#include "gamescanner.h" +#include "gameparser.h" +#include "hlswserver.h" +#include "timerservice.h" +#include "modulelist.h" + +#include "mod_example.h" + +#define DEFAULT_CONFIG "hlswmaster.conf" +#define DEFAULT_LOGFILE "hlswmaster.log" + +static struct option opts[] = { + {"config", 1, 0, 'c'}, + {"debug", 0, 0, 'd'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + LogSystem::init(LOG_DEBUG, new StdErrLog()); + + int arg = 0, code = 0, debug = 0; + char *configfile = DEFAULT_CONFIG; + while (code != -1) { + code = getopt_long(argc, argv, "c:dh", opts, &arg); + + switch (code) { + case 'c': /* config */ + configfile = optarg; + break; + + case 'd': /* debug */ + debug = 1; + break; + + case 'h': /* help */ + printf("Usage: hlsw-master [options]\n" + "Options: \n" + " --config -c configfile use this configfile\n" + " --debug -d do not fork and log to stderr\n" + " --help -h this help\n" + "\n"); + exit(0); + break; + + case '?': /* error */ + exit(-1); + break; + + default: /* unknown / all options parsed */ + break; + } + } + + Config conf; + conf.parseFile(configfile); + +// char* logfile = conf.getString("global", "logfile", DEFAULT_LOGFILE); +// int logprio = conf.getInteger("global", "logprio", LOG_NOTICE); +// LogSystem::init(logprio, new FileLog(logfile)); + + LogSystem::log(LOG_EVERYTIME, "hlswmaster-ng startup"); +// conf.show(); + + ModuleList modList; + + GameScanner scanner(conf, modList); + GameParser parser(conf, scanner, modList); + HlswServer server(conf, parser); + + modList.reg(new ModExample(conf)); + + server.start(); + parser.start(); + scanner.start(); + + while (1) { + TimerService::checkTimers(); + usleep(500000); + + if (!scanner.isRunning()) { + LogSystem::log(LOG_CRIT, "Scanner aborted!"); + break; + } + + if (!parser.isRunning()) { + LogSystem::log(LOG_CRIT, "RecvParser aborted!"); + break; + } + + if (!server.isRunning()) { + LogSystem::log(LOG_CRIT, "HlswServer aborted!"); + break; + } + } + + return -1; +} diff --git a/hlswserver.cpp b/hlswserver.cpp new file mode 100644 index 0000000..3ef9575 --- /dev/null +++ b/hlswserver.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "logging.h" +#include "hlswserver.h" + +#define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00" +#define HLSW_HEADER_LEN 0x12 +#define HLSW_MASTER_PORT 7140 + +HlswServer::HlswServer(Config& conf, GameList& slist) +{ + struct sockaddr_in dst; + char *ip; + + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + LogSystem::log(LOG_CRIT, "HlswServer(): socket()"); + return; + } + + ip = conf.getString("global", "master_ip", "0.0.0.0"); + LogSystem::log(LOG_NOTICE, "HlswServer listen on %s:%d", ip, HLSW_MASTER_PORT); + + dst.sin_family = AF_INET; + dst.sin_port = htons(HLSW_MASTER_PORT); + inet_aton(ip, &dst.sin_addr); + + if (bind(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { + LogSystem::log(LOG_WARNING, "HlswServer(): bind()"); + return; + } + + out = new HlswPacket(); +} + +HlswServer::~HlswServer() +{ + close(sock); +} + +int HlswServer::execute(void* arg) +{ + struct sockaddr_in src; + unsigned char buf[32]; + socklen_t len; + int ret; + + while (1) { + /* auf clientanfrage warten */ + len = sizeof(src); + ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&src, &len); + if (ret != HLSW_HEADER_LEN) { + LogSystem::log(LOG_WARNING, "HlswServer: invalid packet"); + continue; + } + + /* testen ob es sich um ein HLSW anforderung handelt */ + if (memcmp(buf, HLSW_HEADER, HLSW_HEADER_LEN)) { + LogSystem::log(LOG_WARNING, "HlswServer: not a hlsw packet"); + continue; + } + + if (out != NULL && out->count > 0) { + HlswPacket* pkt; + for (pkt = out; pkt != NULL; pkt = pkt->next) { + sendto(sock, pkt->data, pkt->getSize(), 0, + (struct sockaddr *)&src, sizeof(src)); + } + } + } + + return 0; +} diff --git a/hlswserver.h b/hlswserver.h new file mode 100644 index 0000000..5662081 --- /dev/null +++ b/hlswserver.h @@ -0,0 +1,46 @@ +#ifndef _HLSWSERVER_H_ +#define _HLSWSERVER_H_ + +#include + +#include "thread.h" +#include "config.h" +#include "gamelist.h" + + +class HlswServer : public Thread { +public: + HlswServer(Config& conf, GameList& slist); + ~HlswServer(); + + int execute(void* arg); + +protected: + HlswServer(const HlswServer& hs); + HlswServer& operator=(const HlswServer& hs); + +private: + class HlswPacket { + public: + HlswPacket() : next(0), count(0) + { + memset(data, 0, sizeof(data)); + } + + int getSize() + { + return 12 + count * 10; + } + + void addGame(void* ptr) {} + + HlswPacket* next; + int count; + char data[1412]; + }; + + HlswPacket* out; + int sock; +}; + +#endif // _HLSWSERVER_H_ diff --git a/list.h b/list.h new file mode 100644 index 0000000..49b6d21 --- /dev/null +++ b/list.h @@ -0,0 +1,167 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +#include "mutex.h" + +template +class Iterator { +public: + virtual ~Iterator() {} + virtual bool hasNext() const =0; + virtual T* next() =0; + +protected: + Iterator() {} + Iterator(const Iterator& it); + Iterator& operator=(const Iterator& it); +}; + +template +class List { +public: + List() {} + + ~List() + { + AutoMutex am(mutex); + while(!head.isEmpty()) + head.getNext()->del(); + } + + 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); + +private: + template + class ListEntry { + public: + ListEntry(const TT* part = 0) + : prev(this), next(this), part(part) + { + } + + 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; + }; + + ListEntry head; + Mutex mutex; + + template + class ListIterator : public Iterator { + public: + ListIterator(const List* list) + : list(list) + { + // ((ListBase*)list)->mutex.lock(); + pos = &list->head; + } + + ~ListIterator() + { + // ((ListBase*)list)->mutex.unlock(); + } + + bool hasNext() const + { + return (pos->getNext() != &list->head); + } + + TT* next() + { + pos = pos->getNext(); + return (TT*)pos->getPart(); + } + + protected: + ListIterator(const ListIterator& lit); + ListIterator& operator=(const ListIterator& lit); + + private: + const List* list; + const ListEntry* pos; + }; +}; + + +#endif //_LIST_H_ diff --git a/logging.cpp b/logging.cpp new file mode 100644 index 0000000..b5d2e82 --- /dev/null +++ b/logging.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include + +#include "logging.h" + +#define BUFLEN 1024 + + +LogSystem::LogSystem() : output(0), priority(0) +{ + buffer = new char[BUFLEN]; +} + +LogSystem::~LogSystem() +{ + delete buffer; + + if (output) + delete output; +} + +LogSystem* LogSystem::getInstance() +{ + static LogSystem ls; + return &ls; +} + +void LogSystem::init(int prio, LogOutput* out) +{ + LogSystem* ls = getInstance(); + + if (ls->output) + delete ls->output; + + ls->output = out; + ls->priority = prio; +} + +void LogSystem::log(int prio, const char* fmt, ...) +{ + LogSystem* ls = getInstance(); + + if (prio > ls->priority || !ls->output) + return; + + va_list az; + int len; + + va_start(az, fmt); + len = vsnprintf(ls->buffer, BUFLEN, fmt, az); + va_end(az); + + if (errno) { + strncpy(ls->buffer + len, ": ", BUFLEN - len); + len += 2; + strncpy(ls->buffer + len, strerror(errno), BUFLEN - len); + + errno = 0; + } + + ls->output->write(ls->buffer); +} + +/* ------- */ + +void StdErrLog::write(const char* buf) +{ + fprintf(stderr, "%s\n", buf); +} + +/* ------- */ + +FileLog::FileLog(const char* filename) +{ + if (!(logfile = fopen(filename, "a" ))) + LogSystem::log(LOG_CRIT, "Can not open logfile '%s'", filename); + + fprintf(logfile, "-----------------\n"); +} + +FileLog::~FileLog() +{ + fclose(logfile); +} + +void FileLog::write(const char* buf) +{ + time_t tzgr; + char tbuf[64]; + time(&tzgr); + strftime(tbuf, sizeof(tbuf), "%b %d %H:%M:%S :", localtime(&tzgr)); + + fprintf(logfile, "%s %s\n", tbuf, buf); + fflush(logfile); +} diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..bbf74ea --- /dev/null +++ b/logging.h @@ -0,0 +1,63 @@ +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include + +// really noisy debugging (pkt-dumps) +#define LOG_DEBUG 5 + +// normal "debug" (config, recv. packets) +#define LOG_INFO 4 + +// interesting stuff +#define LOG_NOTICE 3 + +// something is not right, but programm is still working (config errors) +#define LOG_WARN 2 + +// something is *really* bad, but we try to keep up (mem-allocs) +#define LOG_ERROR 1 + +// we must bailout *now* +#define LOG_CRIT 0 + +#define LOG_EVERYTIME 0 + +class LogOutput { +public: + virtual ~LogOutput() {}; + virtual void write(const char* buf) {}; +}; + +class LogSystem { +public: + static void init(int prio, LogOutput* lo); + static void log(int prio, const char* fmt, ...); + +private: + LogSystem(); + ~LogSystem(); + static LogSystem* getInstance(); + + LogOutput *output; + int priority; + char* buffer; +}; + +class StdErrLog : public LogOutput { +public: + void write(const char* buf); +}; + +class FileLog : public LogOutput { +public: + FileLog(const char* filename); + ~FileLog(); + + void write(const char* buf); + +private: + FILE* logfile; +}; + +#endif // _LOGGING_H_ diff --git a/mod_example.h b/mod_example.h new file mode 100644 index 0000000..d7fc8e2 --- /dev/null +++ b/mod_example.h @@ -0,0 +1,35 @@ +#ifndef _MODEXAMPLE_H_ +#define _MODEXAMPLE_H_ + +#include "modulelist.h" +#include "logging.h" + +class ModExample : public Module { +public: + ModExample(Config& conf) + { + } + + ~ModExample() + { + } + + void scan(MultiSock* msock) + { + LogSystem::log(LOG_DEBUG, "ModExample::scan()"); + } + + int parse(NetPkt* pkt, GameList* slist) + { + LogSystem::log(LOG_DEBUG, "ModExample::parse()"); + return 0; + } + + char* getName() { + return "Example Module"; + } + +protected: +}; + +#endif // _MODEXAMPLE_H_ diff --git a/modulelist.cpp b/modulelist.cpp new file mode 100644 index 0000000..51786d2 --- /dev/null +++ b/modulelist.cpp @@ -0,0 +1,42 @@ +#include "logging.h" +#include "modulelist.h" + +ModuleList::ModuleList() +{ +} + +ModuleList::~ModuleList() +{ + while (!mlist.isEmpty()) + delete mlist.get(); +} + +void ModuleList::reg(Module* mod) +{ + LogSystem::log(LOG_INFO, "Loading Module '%s'", mod->getName()); + mlist.addTail(mod); +} + +void ModuleList::scan(MultiSock* msock) +{ + Iterator *it = mlist.createIterator(); + while (it->hasNext()) + it->next()->scan(msock); + + delete it; +} + +int ModuleList::parse(NetPkt* pkt, GameList* slist) +{ + int retval = PKT_REJECT; + + Iterator *it = mlist.createIterator(); + while (it->hasNext()) { + retval = it->next()->parse(pkt, slist); + if (retval != PKT_REJECT) + break; + } + + delete it; + return retval; +} diff --git a/modulelist.h b/modulelist.h new file mode 100644 index 0000000..7d2ed62 --- /dev/null +++ b/modulelist.h @@ -0,0 +1,40 @@ +#ifndef _MODULELIST_H_ +#define _MODULELIST_H_ + +#include "multisock.h" +#include "netpkt.h" +#include "gamelist.h" +#include "config.h" + +class Module { +public: + virtual ~Module() {}; + + virtual void scan(MultiSock* msock) =0; + virtual int parse(NetPkt* pkt, GameList* slist) =0; + virtual char* getName() =0; + +protected: + Module() {}; +}; + + +class ModuleList { +public: + ModuleList(); + ~ModuleList(); + + void reg(Module* mod); + + void scan(MultiSock* msock); + int parse(NetPkt* pkt, GameList* slist); + +protected: + ModuleList(const ModuleList& ml); + ModuleList& operator=(const ModuleList& ml); + +private: + List mlist; +}; + +#endif // _MODULELIST_H_ diff --git a/multisock.cpp b/multisock.cpp new file mode 100644 index 0000000..1be3162 --- /dev/null +++ b/multisock.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "multisock.h" + +#define DEFAULT_PORT 7130 + +#define DEVFILE "/proc/net/dev" +#define BUFSIZE 256 + + +MultiSock::Socket::Socket() +{ + if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) + LogSystem::log(LOG_CRIT, "Socket: socket()"); +} + +MultiSock::Socket::~Socket() +{ + close(fd); +} + +bool MultiSock::Socket::bindToDevice(const char* name) +{ + strncpy(devname, name, sizeof(devname)); + + int ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, devname, sizeof(devname)); + + if (ret < 0) { + LogSystem::log(LOG_WARNING, "Socket: setsockopt(SO_BINDTODEVICE) %s", devname); + return false; + } + + return true; +} + +bool MultiSock::Socket::bindToPort(int port) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) { + LogSystem::log(LOG_WARNING, "Socket: ioctl(SIOCGIFADDR) %s", devname); + return false; + } + + memcpy(&addr, &ifr.ifr_addr, sizeof(addr)); + addr.sin_port = htons(port); + + if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + LogSystem::log(LOG_WARNING, "Socket: bind() %s", devname); + return false; + } + + int bcast = 1; + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) { + LogSystem::log(LOG_WARNING, "Socket: setsockopt(SO_BROADCAST) %s", devname); + return false; + } + + return true; +} + + +int MultiSock::Socket::show(char* buf, int size) +{ + return snprintf(buf, size, "%s (%s:%d) ", devname, + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); +} + +MultiSock::Socket* MultiSock::Socket::createSocket(const char* name, int port) +{ + Socket* sock = new Socket(); + + if (sock->fd < 0) { + delete sock; + return NULL; + } + + struct ifreq ifr; + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(sock->fd, SIOCGIFFLAGS, &ifr) != 0) { + LogSystem::log(LOG_WARNING, "Socket: ioctl(SIOCGIFFLAGS) %s", name); + delete sock; + return NULL; + } + + if (!(ifr.ifr_flags & IFF_UP) || (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { + delete sock; + return NULL; + } + + sock->bindToDevice(name); + + if (!sock->bindToPort(port)) { + delete sock; + return NULL; + } + + return sock; +} + + +MultiSock::MultiSock(Config& conf) +{ + char* buf = (char*)malloc(BUFSIZE); + if (buf == NULL) { + LogSystem::log(LOG_CRIT, "MultiSock(): out of memory"); + return; + } + + FILE* fp = fopen(DEVFILE, "r"); + if (fp == NULL) { + LogSystem::log(LOG_CRIT, "MultiSock(): can not open " DEVFILE); + free(buf); + return; + } + + fgets(buf, BUFSIZE, fp); + fgets(buf, BUFSIZE, fp); + + int port = conf.getInteger("global", "scan_port", DEFAULT_PORT); + FD_ZERO(&fdsel); + + while (fgets(buf, BUFSIZE, fp) != NULL) { + char* tok = strtok(buf, " :"); + + // TODO: check against config list + + Socket* sock = Socket::createSocket(tok, port); + if (sock) { + FD_SET(sock->getFD(), &fdsel); + + sock->show(buf, BUFSIZE); + LogSystem::log(LOG_NOTICE, "adding Interface %s", buf); + + ifaceList.add(sock); + } + } + + fclose(fp); + free(buf); +} + +MultiSock::~MultiSock() +{ + while (!ifaceList.isEmpty()) + delete ifaceList.get(); +} + + +int MultiSock::getRecvSocket() +{ + fd_set fdcpy; + + while (1) { + memcpy(&fdcpy, &fdsel, sizeof(fdcpy)); + + select(FD_SETSIZE, &fdcpy, NULL, NULL, NULL); + + Iterator* it = ifaceList.createIterator(); + while (it->hasNext()) { + Socket* sock = it->next(); + if (FD_ISSET(sock->getFD(), &fdcpy)) { + delete it; + return sock->getFD(); + } + } + delete it; + + LogSystem::log(LOG_ERROR, "getRecvSocket(): select()"); + } + + return -1; +} + diff --git a/multisock.h b/multisock.h new file mode 100644 index 0000000..4953d77 --- /dev/null +++ b/multisock.h @@ -0,0 +1,53 @@ +#ifndef _MULTISOCK_H_ +#define _MULTISOCK_H_ + +#include +#include + +#include "list.h" +#include "config.h" + +class MultiSock { +public: + MultiSock(Config& conf); + ~MultiSock(); + + int send(struct in_addr *dstip, int dport, char* data, int size); + int getRecvSocket(); + +protected: + MultiSock(const MultiSock& x); + MultiSock& operator=(const MultiSock& x); + +private: + class Socket { + public: + static Socket* createSocket(const char* name, int port); + int show(char* buf, int size); + + int getFD() { return fd; } + + ~Socket(); + + protected: + Socket(const Socket& s); + Socket& operator=(const Socket& s); + + private: + Socket(); + + bool bindToDevice(const char* name); + bool bindToPort(int port); + + int fd; + char devname[IFNAMSIZ]; + struct sockaddr_in addr; + }; + + void addIface(const char* name, int port); + + List ifaceList; + fd_set fdsel; +}; + +#endif // _MULTISOCK_H_ diff --git a/mutex.h b/mutex.h new file mode 100644 index 0000000..d8e069c --- /dev/null +++ b/mutex.h @@ -0,0 +1,35 @@ +#ifndef _MUTEX_H_ +#define _MUTEX_H_ + +#include + +class Mutex { +public: + Mutex() { pthread_mutex_init(&m, 0); } + ~Mutex() { pthread_mutex_destroy(&m); } + void lock() { pthread_mutex_lock(&m); } + void unlock() { pthread_mutex_unlock(&m); } + +protected: + Mutex(const Mutex& m); + Mutex& operator=(const Mutex& m); + +private: + pthread_mutex_t m; +}; + + +class AutoMutex { +public: + AutoMutex(Mutex& x) : m(x) { m.lock(); } + ~AutoMutex() { m.unlock(); } + +protected: + AutoMutex(const AutoMutex& am); + AutoMutex& operator=(const AutoMutex& am); + +private: + Mutex& m; +}; + +#endif //_MUTEX_H_ diff --git a/netpkt.cpp b/netpkt.cpp new file mode 100644 index 0000000..cb359c5 --- /dev/null +++ b/netpkt.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "netpkt.h" + +NetPkt::NetPkt(int size) +:size(size) +{ + LogSystem::log(LOG_DEBUG, "NetPkt()"); + data = (char*)malloc(size); +} + +NetPkt::~NetPkt() +{ + free(data); + LogSystem::log(LOG_DEBUG, "~NetPkt()"); +} + +int NetPkt::readFromSocket(int fd) +{ + socklen_t i = sizeof(struct sockaddr_in); + return recvfrom(fd, this->data, this->size, 0, (struct sockaddr *)&this->addr, &i); +} + +int NetPkt::show(char* buf, int size) +{ + return snprintf(buf, size, "(%s:%d) %d bytes", + inet_ntoa(this->addr.sin_addr), + ntohs(this->addr.sin_port), + this->size); +} + +NetPkt* NetPkt::createFromSocket(int fd) +{ + int recvsize = 0; + + if (ioctl(fd, FIONREAD, &recvsize) == -1) { + LogSystem::log(LOG_WARNING, "NetPkt::createFromSocket()"); + return NULL; + } + + NetPkt* retval = new NetPkt(recvsize); + retval->readFromSocket(fd); + + return retval; +} diff --git a/netpkt.h b/netpkt.h new file mode 100644 index 0000000..3adcc94 --- /dev/null +++ b/netpkt.h @@ -0,0 +1,32 @@ +#ifndef _NETPKT_H_ +#define _NETPKT_H_ + +#include +#include + +#define PKT_ACCEPT 1 +#define PKT_ACCEPT_FREED 2 +#define PKT_REJECT 4 + +class NetPkt { +public: + ~NetPkt(); + + static NetPkt* createFromSocket(int fd); + NetPkt* append(NetPkt* pkt); + int show(char* buf, int size); + +protected: + NetPkt(const NetPkt& x); + NetPkt& operator=(const NetPkt& x); + +private: + NetPkt(int size); + int readFromSocket(int fd); + + struct sockaddr_in addr; + int size; + char *data; +}; + +#endif // _NETPKT_H_ diff --git a/recvqueue.h b/recvqueue.h new file mode 100644 index 0000000..5ae7b7d --- /dev/null +++ b/recvqueue.h @@ -0,0 +1,15 @@ +#ifndef _RECVQUEUE_H_ +#define _RECVQUEUE_H_ + +#include "netpkt.h" + +class RecvQueue { +public: + virtual ~RecvQueue() {}; + virtual NetPkt* getPkt() =0; + +protected: + RecvQueue() {}; +}; + +#endif // _RECVQUEUE_H_ diff --git a/semaphore.h b/semaphore.h new file mode 100644 index 0000000..a1cb350 --- /dev/null +++ b/semaphore.h @@ -0,0 +1,21 @@ +#ifndef _SEMAPHORE_H_ +#define _SEMAPHORE_H_ + +#include + +class Semaphore { +public: + Semaphore(int i) { sem_init(&s, 0, i); } + ~Semaphore() { sem_destroy(&s); } + void wait() { sem_wait(&s); } + void post() { sem_post(&s); } + +protected: + Semaphore(const Semaphore& s); + Semaphore& operator=(const Semaphore& s); + +private: + sem_t s; +}; + +#endif //_SEMAPHORE_H_ diff --git a/thread.cpp b/thread.cpp new file mode 100644 index 0000000..fd22759 --- /dev/null +++ b/thread.cpp @@ -0,0 +1,77 @@ +#include "thread.h" + +#define THREAD_STACKSIZE 65536 + +#define THREAD_READY 0x01 +#define THREAD_RUNNING 0x02 +#define THREAD_ENDED 0x03 + + +Thread::Thread() +: arg(0), tid(0), state(THREAD_READY) +{} + +Thread::~Thread() +{ + switch (state) { + case THREAD_RUNNING: + cancel(); + + case THREAD_ENDED: + join(); + + case THREAD_READY: + break; + } +} + +int Thread::start(void* arg_) +{ + pthread_attr_t attr; + + if (state != THREAD_READY) + return -1; + + arg = arg_; + state = THREAD_RUNNING; + + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, THREAD_STACKSIZE); + + if (pthread_create(&tid, &attr, (Thread::entry), this) != 0) { + state = THREAD_READY; + return -1; + } + + return 0; +} + +void Thread::cancel() +{ + pthread_cancel(tid); + state = THREAD_ENDED; +} + +bool Thread::isRunning() const +{ + return (state == THREAD_RUNNING); +} + +int Thread::join() +{ + void* retval; + pthread_join(tid, &retval); + state = THREAD_READY; + + return (int)retval; +} + +void* Thread::entry(void* myself) +{ + Thread* thread = (Thread*)myself; + + int retval = thread->execute(thread->arg); + thread->state = THREAD_ENDED; + + return (void*)retval; +} diff --git a/thread.h b/thread.h new file mode 100644 index 0000000..20d0e7e --- /dev/null +++ b/thread.h @@ -0,0 +1,30 @@ +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include + +class Thread { +public: + Thread(); + virtual ~Thread(); + + virtual int execute(void* arg) =0; + + int start(void* arg = 0); + void cancel(); + int join(); + bool isRunning() const; + +protected: + Thread(const Thread& t); + Thread& operator=(const Thread& t); + +private: + void* arg; + pthread_t tid; + volatile int state; + + static void* entry(void* myself); +}; + +#endif // _THREAD_H_ diff --git a/timerservice.cpp b/timerservice.cpp new file mode 100644 index 0000000..eb80af0 --- /dev/null +++ b/timerservice.cpp @@ -0,0 +1,71 @@ +#include + +#include "timerservice.h" + +Timer::Timer(Command* cmd, unsigned int timeval) +: next(0), cmd(cmd), timeval(timeval) +{ +} + +Timer::~Timer() +{ + delete cmd; +} + +TimerService::~TimerService() +{ + Timer* search = firstTimer; + while (search != NULL) { + Timer* next = search->next; + delete search; + search = next; + } +} + +void TimerService::addTimer(Timer* te, bool trigger) +{ + te->timeout = time(NULL) + ((!trigger) ? te->timeval : 0); + + Timer** search = &firstTimer; + while ((*search) != NULL && (*search)->timeout <= te->timeout) + search = &((*search)->next); + + te->next = (*search); + (*search) = te; +} + +void TimerService::checkTimeouts() +{ + long now = time(NULL); + Timer* search = firstTimer; + while (search != NULL && search->timeout <= now) { + search->cmd->execute(); + + firstTimer = search->next; + addTimer(search, false); + + search = firstTimer; + } +} + +TimerService* TimerService::getInstance() +{ + static TimerService ts; + return &ts; +} + +void TimerService::registerTimer(Timer* te) +{ + TimerService* ts = getInstance(); + + AutoMutex am(ts->mutex); + ts->addTimer(te, true); +} + +void TimerService::checkTimers() +{ + TimerService* ts = getInstance(); + + AutoMutex am(ts->mutex); + ts->checkTimeouts(); +} diff --git a/timerservice.h b/timerservice.h new file mode 100644 index 0000000..1bb11db --- /dev/null +++ b/timerservice.h @@ -0,0 +1,54 @@ +#ifndef _TIMERSERVICE_H_ +#define _TIMERSERVICE_H_ + +#include "mutex.h" + +class Command { +public: + virtual ~Command() {}; + virtual void execute() =0; + +protected: + Command() {}; +}; + +class Timer { +friend class TimerService; +public: + Timer(Command* cmd, unsigned int timeval); + ~Timer(); + +protected: + Timer(const Timer& te); + Timer& operator=(const Timer& te); + +private: + Timer* next; + Command* cmd; + int timeval; + long timeout; +}; + +class TimerService { +public: + static void registerTimer(Timer* te); + static void checkTimers(); + +protected: + TimerService(const TimerService& ts); + TimerService& operator=(const TimerService& ts); + +private: + TimerService() {} + ~TimerService(); + + static TimerService* getInstance(); + + void addTimer(Timer* te, bool trigger); + void checkTimeouts(); + + Timer* firstTimer; + Mutex mutex; +}; + +#endif // _TIMERSERVICE_H_