commit ba91546d887ca65b80b56e1428bbbad82d40c224 Author: Olaf Rempel Date: Thu Feb 2 16:55:44 2006 +0100 Initial Commit 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_