commit c4d38c9cd0f22206ccdb3fca85e41f4a012daa15 Author: Olaf Rempel Date: Thu Feb 2 16:24:06 2006 +0100 Version 0.10 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..85105ca --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +# Toplevel Makefile + +MASTER_SRC := src/client.c src/daemon.c src/logging.c src/main.c src/plugin.c src/plugin_helper.c src/scanner.c src/serverlist.c +PLUGIN_SRC := plugins/quake3.c + +CFLAGS := -Wall -I. -I./include -g -fPIC + +DEPFILES := $(PLUGIN_SRC:%.c=%.d) $(MASTER_SRC:%.c=%.d) + +# ############################ + +all: $(DEPFILES) $(PLUGIN_SRC:%.c=%.so) src/hlswmaster + +src/hlswmaster: $(MASTER_SRC:%.c=%.o) + gcc -ldl -lpthread -rdynamic $^ -o $@ + +%.d: %.c + @-$(CC) -M -MG $(CFLAGS) $< > $@ + +%.o: %.c %.d + $(CC) $(CFLAGS) -o $@ -c $< + +%.so: %.o + $(LD) -shared -o $@ $< + +clean: + rm -rf src/hlswmaster src/*.o plugins/*.so plugins/*.o $(DEPFILES) + +-include $(DEPFILES) diff --git a/include/hlswmaster.h b/include/hlswmaster.h new file mode 100644 index 0000000..d387153 --- /dev/null +++ b/include/hlswmaster.h @@ -0,0 +1,48 @@ +#ifndef _HLSWMASTER_H +#define _HLSWMASTER_H + +#include "list.h" +#include "netpkt.h" + +struct game_server { + /* must be first */ + struct list_head list; + + u_int16_t gameid; + u_int32_t ip; + u_int16_t port1; + u_int16_t port2; + + unsigned long modtime; +}; + +/* daemon.c */ +void daemonize(char *pw_name); + +/* logging.c */ +void log_open(char *logfile); +void log_close(); +void log_print(const char *fmt, ... ); + +/* plugin.c */ +int plugin_load(char *name); +int plugin_unload(char *name); +int plugins_scan(void); +int plugins_parse(struct net_pkt *pkt); +int plugins_gc(unsigned long timeout); + +/* scanner.c */ +int scan_init(void); +void scan_exit(void); +void scan_transmit(void); +void scan_receive(void); + +/* serverlist.c */ +void server_collector(void); + +/* client.c */ +int client_pkt_add(struct game_server *server); +int client_pkt_commit(void); +void client_handler(void); + +#endif diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..b670935 --- /dev/null +++ b/include/list.h @@ -0,0 +1,267 @@ +#ifndef _LIST_H +#define _LIST_H + +/* +** stolen from linux kernel (2.6.11) +** linux/include/linux/stddef.h (offsetoff) +** linux/include/linux/kernel.h (container_of) +** linux/include/linux/list.h (*list*) +** linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND) +** +** modified by Olaf Rempel +*/ +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/** + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif + +/* Return pointer to first true entry, if any, or NULL. A macro + required to allow inlining of cmpfn. */ +#define LIST_FIND(head, cmpfn, type, args...) \ +({ \ + const struct list_head *__i, *__j = NULL; \ + \ + list_for_each(__i, (head)) \ + if (cmpfn((const type)__i , ## args)) { \ + __j = __i; \ + break; \ + } \ + (type)__j; \ +}) diff --git a/include/netpkt.h b/include/netpkt.h new file mode 100644 index 0000000..b5e29ba --- /dev/null +++ b/include/netpkt.h @@ -0,0 +1,20 @@ +#ifndef _NETPKT_H +#define _NETPKT_H + +#include +#include +#include + +#include "list.h" + +struct net_pkt { + /* must be first */ + struct list_head list; + + struct sockaddr_in addr; + unsigned int size; + unsigned char buf[0]; + +}; + +#endif diff --git a/include/plugin.h b/include/plugin.h new file mode 100644 index 0000000..1d3c9fd --- /dev/null +++ b/include/plugin.h @@ -0,0 +1,31 @@ +#ifndef _PLUGIN_H +#define _PLUGIN_H + +#include "netpkt.h" +#include "list.h" + +extern void pkt_queue(struct net_pkt *pkt); +extern int server_add(unsigned int gameid, struct in_addr ip, u_int16_t port1, u_int16_t port2); + +extern struct net_pkt * pkt_factory(char *dstip, unsigned int dstport, char *buf, unsigned int size); + +extern int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); +extern int pkt_strcmp(struct net_pkt *pkt, unsigned int offset, char *search); + +struct hlswmaster_plugin { + /* must be first */ + struct list_head list; + + char name[32]; + + int (*init)(struct list_head *config); + int (*fini)(void); + int (*scan)(void); + int (*parse)(struct net_pkt *pkt); + int (*gc)(int timeout); +}; + +extern void register_plugin(struct hlswmaster_plugin *me); +extern void unregister_plugin(struct hlswmaster_plugin *me); + +#endif diff --git a/plugins/quake3.c b/plugins/quake3.c new file mode 100644 index 0000000..41c1a95 --- /dev/null +++ b/plugins/quake3.c @@ -0,0 +1,49 @@ +#include "plugin.h" + +#define GAMEID_Q3 0x0006 + +int scan(void) { + struct net_pkt *scan; + unsigned int port; + char msg[] = "\xff\xff\xff\xffgetstatus"; + + for (port = 27960; port <= 27963; port++) { + if (!(scan = pkt_factory(NULL, port, msg, 13))) + return 0; + + pkt_queue(scan); + } + return 1; +} + +int parse(struct net_pkt *pkt) { + if (!pkt_strcmp(pkt, 0, "\xff\xff\xff\xffstatusResponse")) { + server_add(GAMEID_Q3, pkt->addr.sin_addr, pkt->addr.sin_port, 0); + return 1; + } + return 0; +} + +int init(struct list_head *config) { + return 1; +} + +int fini(void) { + return 1; +} + +static struct hlswmaster_plugin quake3_plugin = { + .name = "quake3", + .init = &init, + .fini = &fini, + .scan = &scan, + .parse = &parse, +}; + +void _init(void) { + register_plugin(&quake3_plugin); +} + +void _fini(void) { + unregister_plugin(&quake3_plugin); +} diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..261e5d0 --- /dev/null +++ b/src/client.c @@ -0,0 +1,197 @@ +/*************************************************************************** + * Copyright (C) 03/2005 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hlswmaster.h" +#include "plugin.h" +#include "list.h" + +#define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00" +#define HLSW_HEADER_LEN 0x12 +#define MAX_PKT_LEN (1400 + HLSW_HEADER_LEN) + +struct client_pkt { + struct list_head list; + unsigned int size; + unsigned char buf[0]; +}; + +/* real und prepare list. enthalten die client-antworten */ +LIST_HEAD(client_pkt_list); +LIST_HEAD(client_pkt_prepare); + +/* sichert die real list ab */ +static pthread_mutex_t pkt_list_lock = PTHREAD_MUTEX_INITIALIZER; + +/** + * pkt_not_full() + * LIST_FIND helper + * + * @param struct client_pkt *a + * @param void *b + * @return true wenn das paket noch nicht voll ist + */ +static inline int pkt_not_full(const struct client_pkt *a, void *b) { + return (a->size < MAX_PKT_LEN); +} + +/** + * client_pkt_add_real() + * erzeugt eine neues client_pkt und fuegt es einer liste hinzu + * gibt einen pointer auf das neue client_pkt zurueck + * + * @param struct list_head *list + * @return struct client_pkt * or NULL on error + */ +static struct client_pkt * client_pkt_add_real(struct list_head *list) { + struct client_pkt *new; + + if (!(new = malloc(sizeof(struct client_pkt) + MAX_PKT_LEN))) + return NULL; + + INIT_LIST_HEAD(&new->list); + + new->size = HLSW_HEADER_LEN; + memcpy(new->buf, HLSW_HEADER, HLSW_HEADER_LEN); + + list_add_tail(&new->list, list); + return new; +} + +/** + * client_pkt_add() + * fuegt dem freien client_pkt der prepare-liste ein Spiel hinzu + * wenn kein platz vorhanden ist, wird ein neues paket erzeugt + * + * @param struct game_server *server + * @return false bei fehler (malloc) + * + * TODO: memcpy() durch pointer ops ersetzen? + */ +int client_pkt_add(struct game_server *server) { + struct client_pkt *pkt; + + char *src, *dst; + + pkt = LIST_FIND(&client_pkt_prepare, pkt_not_full, struct client_pkt *, NULL); + if (!pkt && !(pkt = client_pkt_add_real(&client_pkt_prepare))) + return 0; + + dst = (char *)&pkt->buf + pkt->size; + + src = (char *)&server->gameid; + memcpy(dst, src, 2); + + src = (char *)&server->ip; + memcpy(dst +2, src, 4); + + src = (char *)&server->port1; + memcpy(dst +6, src, 4); + + src = (char *)&server->port2; + memcpy(dst +8, src, 4); + + pkt->size += 10; + return 1; +} + +/** + * client_pkt_commit() + * + * die prepare liste die echte liste und alle pakete der + * alten liste werden entfernt + * + * @return true + */ +int client_pkt_commit() { + struct list_head old_list, *pkt, *tmp; + + pthread_mutex_lock(&pkt_list_lock); + + /* old_list wird head der real list */ + list_add(&old_list, &client_pkt_list); + list_del(&client_pkt_list); + + /* client_pkt_list wird head der prepare list */ + list_add(&client_pkt_list, &client_pkt_prepare); + list_del_init(&client_pkt_prepare); + + pthread_mutex_unlock(&pkt_list_lock); + + /* alle alten pakete entfernen */ + list_for_each_safe(pkt, tmp, &old_list) { + list_del(pkt); + free(pkt); + } + return 1; +} + +/** + * client_handler() + * empfaengt und beantwortet die HLSW Anfragen + * + * TODO: src-ip configurierbar machen (config file?) + */ +void client_handler(void) { + struct client_pkt *pkt; + struct sockaddr_in dst; + int sock, i; + unsigned char buf[32]; + + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + log_print("client_handler(): socket()"); + return; + } + + dst.sin_family = AF_INET; + dst.sin_port = htons(7140); + inet_aton("0.0.0.0", &dst.sin_addr); + + if (bind(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { + log_print("client_handler(): bind()"); + return; + } + + while (1) { + /* auf clientanfrage warten */ + i = sizeof(struct sockaddr_in); + recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&dst, &i); + + /* testen ob es sich um ein HLSW anforderung handelt */ + if (memcmp(buf, HLSW_HEADER, HLSW_HEADER_LEN)) + continue; + + pthread_mutex_lock(&pkt_list_lock); + + /* unsere vorbereiteten pakete ausgeben */ + list_for_each_entry(pkt, &client_pkt_list, list) + sendto(sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&dst, sizeof(dst)); + + pthread_mutex_unlock(&pkt_list_lock); + } +} diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..07c1508 --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "hlswmaster.h" + +void daemonize(char *pw_name) { + int fail = 0; + pid_t pid; + struct passwd *pwl; + + if ((pid = fork()) < 0) { + log_print("daemonize(): fork()"); + exit(-1); + + } else if (pid != 0) { + exit(0); + + } else { + /* Kindprozess weiterlaufen lassen */ + if (setsid() == -1) { + log_print("daemonize: setsid()"); + exit(-1); + } + + umask(0) ; + + if (pw_name != NULL) { + if((pwl = getpwnam(pw_name)) == NULL) { + log_print("mkdaemon(): getpwnam(\"%s\")", pw_name); + exit(-1); + } + + if (setgid(pwl->pw_gid) != 0) { + log_print("mkdaemon(): setgid()"); + fail = 1; + } + + if (setuid(pwl->pw_uid) != 0) { + log_print("mkdaemon(): setuid()"); + fail = 1; + } + + if (fail == 0) { + log_print("user changed to: %s, pid: %d", pw_name, getpid()); + } + } + } +} diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..1f4e95a --- /dev/null +++ b/src/logging.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +static FILE *log_fd = NULL; + +void log_close() { + fclose(log_fd); +} + +void log_open(char *logfile) { + if ((log_fd = fopen(logfile, "a" )) == NULL) { + fprintf(stderr, "log_open(\"%s\"): %s\n", logfile, strerror(errno)); + exit(-1); + } + if (atexit(log_close) != 0) { + fprintf(stderr, "log_open(): atexit(): %s\n", strerror(errno)); + exit(-1); + } +} + +void log_print(const char *fmt, ...) { + va_list az; + time_t tzgr; + char tbuf[64]; + char buffer[256]; + + va_start(az, fmt); + vsprintf(buffer, fmt, az); + va_end(az); + + time(&tzgr); + strftime(tbuf, 64, "%b %d %H:%M:%S :", localtime(&tzgr)); + + if (log_fd) { + if (errno) { + fprintf(log_fd, "%s %s: %s\n", tbuf, buffer, strerror(errno)); + } else { + fprintf(log_fd, "%s %s\n", tbuf, buffer); + } + } else { + if (errno) { + fprintf(stderr, "%s %s: %s\n", tbuf, buffer, strerror(errno)); + } else { + fprintf(stderr, "%s %s\n", tbuf, buffer); + } + } + errno = 0; + fflush(log_fd); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..df42034 --- /dev/null +++ b/src/main.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include + +#include "hlswmaster.h" + +static struct option opts[] = { + {"config", 1, 0, 'c'}, + {"user", 1, 0, 'u'}, + {"debug", 1, 0, 'd'}, + {"no-daemon", 0, 0, 'f'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + +int main(int argc, char *argv[]) { + int arg = 0, code = 0; + pthread_t thread1, thread2, thread3, thread4; + + while (code != -1) { + code = getopt_long(argc, argv, "c:u:d:fh", opts, &arg); + + switch (code) { + case 'c': /* config */ + break; + + case 'u': /* user */ + break; + + case 'd': /* debug */ + break; + + case 'f': /* no-daemon */ + break; + + case 'h': /* help */ + printf("Usage: hlsw-master [options]\n" + "Options: \n" + " --config -c configfile use this configfile\n" + " --user -u username change uid to username\n" + " --debug -d debuglevel level: 0 - 7\n" + " --no-daemon -f do not fork\n" + " --help -h this help\n" + "\n"); + exit(0); + break; + + case '?': /* error */ + exit(-1); + break; + + default: /* unknown / all options parsed */ + break; + } + } + + + plugin_load("plugins/quake3.so"); + + scan_init(); + + pthread_create(&thread1, NULL, (void *)&scan_transmit, NULL); + pthread_create(&thread2, NULL, (void *)&scan_receive, NULL); + pthread_create(&thread3, NULL, (void *)&server_collector, NULL); + pthread_create(&thread4, NULL, (void *)&client_handler, NULL); + + + sleep(9999); + + scan_exit(); + + return 0; +} diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..e860255 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 03/2005 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include "hlswmaster.h" +#include "plugin.h" +#include "netpkt.h" +#include "list.h" + +/* liste der geladenen plugins */ +LIST_HEAD(plugin_list); + +/* sichert die plugin list UND die plugins ab */ +static pthread_mutex_t plugin_lock = PTHREAD_MUTEX_INITIALIZER; + +int plugin_load(char *name) { + void *tmp; + + if (!(tmp = dlopen(name, RTLD_NOW))) + log_print("unable to load plugin '%s'", name); + + return 0; +} + +int plugin_unload(char *name) { + return 0; +} + +int plugins_scan(void) { + struct hlswmaster_plugin *plugin; + + pthread_mutex_lock(&plugin_lock); + list_for_each_entry(plugin, &plugin_list, list) + if (plugin->scan && !plugin->scan()) + log_print("plugin %s: scan error", plugin->name); + + pthread_mutex_unlock(&plugin_lock); + return 1; +} + +int plugins_parse(struct net_pkt *pkt) { + struct hlswmaster_plugin *plugin; + + pthread_mutex_lock(&plugin_lock); + list_for_each_entry(plugin, &plugin_list, list) { + if (plugin->parse && plugin->parse(pkt)) { + pthread_mutex_unlock(&plugin_lock); + return 1; + } + } + + pthread_mutex_unlock(&plugin_lock); + return 0; +} + +int plugins_gc(unsigned long timeout) { + struct hlswmaster_plugin *plugin; + + pthread_mutex_lock(&plugin_lock); + list_for_each_entry(plugin, &plugin_list, list) + if (plugin->gc) + plugin->gc(timeout); + + pthread_mutex_unlock(&plugin_lock); + return 1; +} + +/* called vom plugin:_init() after local init */ +void register_plugin(struct hlswmaster_plugin *me) { + if (!me->init || (me->init(NULL))) { + pthread_mutex_lock(&plugin_lock); + list_add((struct list_head *)me, &plugin_list); + pthread_mutex_unlock(&plugin_lock); + } +} + +/* called vom plugin:_fini() after local finish */ +void unregister_plugin(struct hlswmaster_plugin *me) { + pthread_mutex_lock(&plugin_lock); + list_del((struct list_head *)me); + pthread_mutex_unlock(&plugin_lock); + + if (me->fini) + me->fini(); +} diff --git a/src/plugin_helper.c b/src/plugin_helper.c new file mode 100644 index 0000000..f833be3 --- /dev/null +++ b/src/plugin_helper.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "netpkt.h" + +struct net_pkt * pkt_factory(char *dstip, unsigned int dstport, char *buf, unsigned int size) { + struct net_pkt *pkt; + + if (!(pkt = malloc(sizeof(struct net_pkt) + size))) + return NULL; + + INIT_LIST_HEAD(&pkt->list); + + pkt->addr.sin_family = AF_INET; + pkt->addr.sin_port = htons(dstport); + + if (dstip) + inet_aton(dstip, &pkt->addr.sin_addr); + else + inet_aton("255.255.255.255", &pkt->addr.sin_addr); + + pkt->size = size; + memcpy(pkt->buf, buf, size); + + return pkt; +} + +int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) { + + if (offset >= pkt->size) + return 1; + + if (offset + size >= pkt->size) + size = pkt->size - offset; + + return memcmp(pkt->buf, search, size); +} + +int pkt_strcmp(struct net_pkt *pkt, unsigned int offset, char *search) { + return pkt_memcmp(pkt, offset, search, strlen(search)); +} diff --git a/src/scanner.c b/src/scanner.c new file mode 100644 index 0000000..e4a6d9e --- /dev/null +++ b/src/scanner.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 03/2005 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "netpkt.h" +#include "hlswmaster.h" + +LIST_HEAD(send_queue); + +static int scan_sock; + +void pkt_queue(struct net_pkt *pkt) { + list_add_tail(&pkt->list, &send_queue); +} + +void scan_transmit(void) { + struct net_pkt *pkt, *tmp; + + while (1) { + plugins_scan(); + + list_for_each_entry_safe(pkt, tmp, &send_queue, list) { + if (sendto(scan_sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&pkt->addr, sizeof(pkt->addr)) < 0) + log_print("scan_transmit(): sendto()"); + + list_del(&pkt->list); + free(pkt); + } + + sleep(30); + } +} + +void scan_receive(void) { + struct net_pkt *pkt; + fd_set fdsel, fdcpy; + int recvsize, i; + + FD_ZERO(&fdsel); + FD_SET(scan_sock, &fdsel); + + while (1) { + memcpy(&fdcpy, &fdsel, sizeof(fdsel)); + select(FD_SETSIZE, &fdcpy, NULL, NULL, NULL); + + ioctl(scan_sock, FIONREAD, &recvsize); + + if (recvsize <= 0) + continue; + + if (!(pkt = malloc(sizeof(struct net_pkt) + recvsize))) + continue; + + i = sizeof(struct sockaddr_in); + pkt->size = recvfrom(scan_sock, pkt->buf, recvsize, 0, (struct sockaddr *)&pkt->addr, &i); + + if (!plugins_parse(pkt)) + log_print("received unknown packet\n"); + + free(pkt); + } +} + +int scan_init() { + struct sockaddr_in dst; + int i; + + if ((scan_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + log_print("scan_init(): socket()"); + return 0; + } + + dst.sin_family = AF_INET; + dst.sin_port = htons(7130); + inet_aton("0.0.0.0", &dst.sin_addr); + + if (bind(scan_sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { + log_print("scan_init(): bind()"); + return 0; + } + + if (setsockopt(scan_sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))) { + log_print("scan_init(): setsockopt()"); + return 0; + } + + return 1; +} + +void scan_exit() { + close(scan_sock); +} diff --git a/src/serverlist.c b/src/serverlist.c new file mode 100644 index 0000000..5edb1f2 --- /dev/null +++ b/src/serverlist.c @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 03/2005 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hlswmaster.h" +#include "plugin.h" +#include "list.h" + +LIST_HEAD(server_list); + +/* sichert die server liste */ +static pthread_mutex_t server_list_lock = PTHREAD_MUTEX_INITIALIZER; + +/** + * server_cmp() + * LIST_FIND helper + * + * @param struct game_server *a + * @param struct game_server *b + * @return true wenn es sich um den selben server handelt + */ +static inline int server_cmp(const struct game_server *a, struct game_server *b) { + return (a->gameid == b->gameid && a->ip == b->ip && + a->port1 == b->port1 && a->port2 == b->port2); +} + +/** + * server_add() + * fuegt der internen serverliste einen server hinzu + * wenn dieser server schon in der liste vorhanden ist, wird nur + * die modtime angepasst + * + * @param unsigned int gameid + * @param struct in_addr ip + * @param u_int16_t port1 + * @param u_int16_t port2 + * @return false bei fehler + */ +int server_add(unsigned int gameid, struct in_addr ip, u_int16_t port1, u_int16_t port2) { + struct game_server server, *nserver; + + server.gameid = gameid; + server.ip = ip.s_addr; + server.port1 = ntohs(port1); + server.port2 = ntohs(port2); + + pthread_mutex_lock(&server_list_lock); + + /* diesen server in der liste suchen */ + nserver = LIST_FIND(&server_list, server_cmp, struct game_server *, &server); + if (!nserver) { + /* neuen eintrag anlegen */ + if (!(nserver = malloc(sizeof(struct game_server)))) { + pthread_mutex_unlock(&server_list_lock); + return 0; + } + + memcpy(nserver, &server, sizeof(struct game_server)); + list_add_tail(&nserver->list, &server_list); + } + /* modtime anpassen */ + nserver->modtime = time(NULL); + + pthread_mutex_unlock(&server_list_lock); + + return 1; +} + +/** + * server_collector() + * loescht alte server aus der liste + * baut aus den verbleibenden die client_liste auf + * + * TODO: timeout und intervall als config parameter (config file?) + */ +void server_collector(void) { + struct game_server *server, *tmp; + unsigned long now, timeout = 120; + + while (1) { + sleep(5); + + now = time(NULL); + pthread_mutex_lock(&server_list_lock); + list_for_each_entry_safe(server, tmp, &server_list, list) { + if (server->modtime + timeout < now) { + list_del(&server->list); + free(server); + continue; + } + client_pkt_add(server); + } + pthread_mutex_unlock(&server_list_lock); + client_pkt_commit(); + } +}