diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8461f27 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +VERSION := v2.00 + +CFLAGS := -O2 -pipe -Wall -DVERSION='"$(VERSION)"' +LDFLAGS := -ldl -rdynamic + +HLSWMASTER_SRC := configfile.o event.o gamelist.o hlswmaster.o logging.o \ + netpkt.o plugin.o plugin_helper.o scanner.o server.o + +PLUGINS := $(wildcard p_*.c) + +all: hlswmaster masterquery $(PLUGINS:.c=.so) + +hlswmaster: $(HLSWMASTER_SRC) + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +masterquery: masterquery.o + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.so: %_sh.o + $(LD) -shared -o $@ $< + +%_sh.o: %.c + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +%.d: %.c + $(CC) $(CFLAGS) -MM -c $< -o $@ + +clean: + rm -f hlswmaster masterquery *.so *.d *.o *.log + +DEPS := $(wildcard *.c) +-include $(DEPS:.c=.d) diff --git a/client.c b/client.c deleted file mode 100644 index faa5bc3..0000000 --- a/client.c +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** - * 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 "configfile.h" -#include "list.h" - -#define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00" -#define HLSW_HEADER_LEN 0x12 -#define HLSW_ENTRY_LEN 10 -#define MAX_PKT_LEN (HLSW_HEADER_LEN + HLSW_ENTRY_LEN * 140) - -struct client_pkt { - struct list_head list; - unsigned int size; - unsigned char buf[0]; -}; - -/** working list (wird zu den clients gesendet) */ -static LIST_HEAD(client_pkt_list); - -/** prepare list (wird im hintergrund zusammengebaut) */ -static LIST_HEAD(client_pkt_prepare); - -/** sichert die work list ab */ -static pthread_mutex_t client_pkt_list_lock = PTHREAD_MUTEX_INITIALIZER; - -/** - * client_pkt_not_full() - * prueft ob ein client-paket komplett belegt ist - * - * @param *cpkt pointer auf paket das getestet wird - * @param *unused - * @return true wenn das paket noch nicht voll ist - */ -static inline int client_pkt_not_full(const struct client_pkt *cpkt, void *unused) -{ - return (cpkt->size <= (MAX_PKT_LEN - HLSW_ENTRY_LEN)); -} - -/** - * client_pkt_add_real() - * erzeugt eine neues client_pkt und fuegt es einer liste hinzu - * - * @param *list liste die das neue paket aufnimmt - * @return pointer auf ein neues paket oder NULL bei fehler - */ -static struct client_pkt * client_pkt_add_real(struct list_head *list) -{ - struct client_pkt *cpkt; - - cpkt = malloc(sizeof(struct client_pkt) + MAX_PKT_LEN); - if (cpkt) { - INIT_LIST_HEAD(&cpkt->list); - - /* kopier den hlsw header in das client paket */ - memcpy(cpkt->buf, HLSW_HEADER, HLSW_HEADER_LEN); - cpkt->size = HLSW_HEADER_LEN; - - /* am anfang eingliedern! (suche nach freien paket schneller) */ - list_add(&cpkt->list, list); - } - return cpkt; -} - -/** - * client_pkt_add() - * fuegt der client_pkt liste einen server hinzu, wenn kein platz - * im aktuellen paket vorhanden ist, wird ein neues paket allokiert - * - * @param *server pointer den server - * @return false bei fehler - */ -int client_pkt_add(struct game_server *server) -{ - struct client_pkt *cpkt; - - /* sucht ein freies paket, oder erzeugt eins */ - cpkt = LIST_FIND(&client_pkt_prepare, client_pkt_not_full, struct client_pkt *, NULL); - if (!cpkt && !(cpkt = client_pkt_add_real(&client_pkt_prepare))) - return 0; - - /* kopiert den server eintrag in das client paket */ - memcpy((void *)&cpkt->buf + cpkt->size, &server->gameid, HLSW_ENTRY_LEN); - cpkt->size += HLSW_ENTRY_LEN; - - return 1; -} - -/** - * client_pkt_commit() - * die prepare liste und die work liste werden ausgetauscht - * alle pakete der alten work liste werden entfernt - * - * @return true - */ -int client_pkt_commit() -{ - struct list_head old_list, *cpkt, *tmp; - - /* wenn die liste leer ist, nur HLSW-header verschicken */ - if (list_empty(&client_pkt_prepare)) - client_pkt_add_real(&client_pkt_prepare); - - pthread_mutex_lock(&client_pkt_list_lock); - - /* old_list wird head der work 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(&client_pkt_list_lock); - - /* alle alten pakete entfernen */ - list_for_each_safe(cpkt, tmp, &old_list) { - list_del(cpkt); - free(cpkt); - } - return 1; -} - -/** - * client_handler() - * empfaengt und beantwortet die HLSW anfragen der clients - */ -void client_handler(void) -{ - struct client_pkt *cpkt; - struct sockaddr_in dst; - int sock, i; - unsigned char buf[32], *ip; - - if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - log_print("client_handler(): socket()"); - return; - } - - ip = config_get_string("global", "master_ip", "0.0.0.0"); - log_print("thread_start: client_handler (%s:7140)", ip); - - dst.sin_family = AF_INET; - dst.sin_port = htons(7140); - inet_aton(ip, &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(&client_pkt_list_lock); - - /* pakete aus der work list senden */ - list_for_each_entry(cpkt, &client_pkt_list, list) - sendto(sock, cpkt->buf, cpkt->size, 0, (struct sockaddr *)&dst, sizeof(dst)); - - pthread_mutex_unlock(&client_pkt_list_lock); - } -} diff --git a/config.c b/config.c deleted file mode 100644 index fefa8bc..0000000 --- a/config.c +++ /dev/null @@ -1,263 +0,0 @@ -/*************************************************************************** - * 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 "hlswmaster.h" -#include "configfile.h" -#include "list.h" - -//#define DEBUG 1 - -/** liste der config-sections */ -static LIST_HEAD(config_list); - -/** - * config_add_section() - * allokiert eine neue section und fuegt sie der config liste hinzu - * - * @param *name name der section - * @return pointer auf die section, oder NULL bei fehler - * - * @todo sectionname ist nicht eindeutig - */ -static struct conf_section * config_add_section(char *name) -{ - struct conf_section *section; - - if (!name) - return NULL; - - section = malloc(sizeof(struct conf_section)); - if (section) { - INIT_LIST_HEAD(§ion->list); - INIT_LIST_HEAD(§ion->tupel); - - strncpy(section->name, name, sizeof(section->name)); - list_add_tail(§ion->list, &config_list); - } - return section; -} - -/** - * config_add_tupel() - * fuegt einer section ein config tupel hinzu - * - * @param *section config section - * @param *option name der option - * @param *parameter wert der option - * @return false bei fehler - */ -static int config_add_tupel(struct conf_section *section, char *option, char *parameter) -{ - struct conf_tupel *tupel; - - if (!section || !option || !parameter) - return 0; - - if (!(tupel = malloc(sizeof(struct conf_tupel)))) - return 0; - - INIT_LIST_HEAD(&tupel->list); - - tupel->option = strdup(option); - tupel->parameter = strdup(parameter); - - list_add_tail(&tupel->list, §ion->tupel); - return 1; -} - -/** - * config_free() - * entfernt die config_liste aus dem Speicher - */ -static void config_free() -{ - struct conf_section *section, *section_tmp; - struct conf_tupel *tupel, *tupel_tmp; - - list_for_each_entry_safe(section, section_tmp, &config_list, list) { - list_for_each_entry_safe(tupel, tupel_tmp, §ion->tupel, list) { - list_del(&tupel->list); - free(tupel->option); - free(tupel->parameter); - free(tupel); - } - list_del(§ion->list); - free(section); - } -} - -/** - * config_parse() - * parsed die configfile - * - * @param *config filename des configfiles - * @return false bei fehler - */ -int config_parse(char *config) -{ - struct conf_section *section = NULL; - FILE *fz; - int i = 0, ret = 1; - char *row, *tok, *tok2; - - if (!config) - return 0; - - if (!(row = malloc(1024))) - return 0; - - if (!(fz = fopen(config, "r"))) { - log_print("config_parse(): %s", config); - return 0; - } - - 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 = config_add_section(tok); - if (!section) { - log_print("config_parse(): invalid section in row %d", i); - ret = 0; - break; - } - continue; - - /* option, aber es gab noch keine section */ - } else if (!section) { - log_print("config_parse(): missing section in row %d", i); - ret = 0; - break; - } - - /* option */ - if ((tok = strtok(row, " \n")) && (tok2 = strtok(NULL, " \n"))) - if (!config_add_tupel(section, tok, tok2)) - log_print("config_parse(): invalid row %d", i); - } - - fclose(fz); - free(row); - - /* beim beenden die liste wieder freigeben */ - if (atexit(config_free) != 0) { - log_print("config_parse(): atexit()"); - return 0; - } - -#ifdef DEBUG - { - struct conf_section *section; - struct conf_tupel *tupel; - - list_for_each_entry(section, &config_list, list) - list_for_each_entry(tupel, §ion->tupel, list) - printf("%s: %s = %s\n", section->name, tupel->option, tupel->parameter); - } -#endif - - return ret; -} - -/** - * config_get_section() - * gibt eine benannte section zurueck - * - * @param *name name der section - * @return pointer auf die section, oder NULL bei fehler - */ -struct conf_section * config_get_section(char *name) -{ - struct conf_section *section; - - list_for_each_entry(section, &config_list, list) { - if (!strcmp(section->name, name)) - return section; - } - return NULL; -} - -/** - * config_get_parameter() - * gibt den parameter der ersten passenden option - * in einer section zurueck - * - * @param *section pointer auf die section - * @param *option option name - * @return pointer auf den parameter string oder NULL bei fehler - */ -char * config_get_parameter(struct conf_section *section, char *option) -{ - struct conf_tupel *tupel; - - list_for_each_entry(tupel, §ion->tupel, list) { - if (!strcmp(tupel->option, option)) - return tupel->parameter; - } - return NULL; -} - -/** - * config_get_string() - * gibt den parameter des section/option tupels zurueck - * - * @param *section name der section - * @param *option name der option - * @param *def default string - * @return parameter string oder default string bei fehler - */ -char * config_get_string(char *section, char *option, char *def) -{ - struct conf_section *tmp; - char *ret; - - tmp = config_get_section(section); - if (tmp && (ret = config_get_parameter(tmp, option))) - return ret; - - return def; -} - -/** - * config_get_int() - * gibt den parameter des section/option tupels zurueck - * - * @param *section name der section - * @param *option name der option - * @param *def default wert - * @return parameter wert oder default wert bei fehler - */ -int config_get_int(char *section, char *option, int def) -{ - char *ret; - - ret = config_get_string(section, option, NULL); - return ret ? atoi(ret) : def; -} diff --git a/configfile.c b/configfile.c new file mode 100644 index 0000000..02c605c --- /dev/null +++ b/configfile.c @@ -0,0 +1,230 @@ +/*************************************************************************** + * Copyright (C) 06/2006 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 "configfile.h" +#include "list.h" +#include "logging.h" + +#define BUFSIZE 1024 + +struct conf_section { + struct list_head list; + struct list_head tupel_list; + char *name; +}; + +struct conf_tupel { + struct list_head list; + char *option; + char *parameter; +}; + +static LIST_HEAD(config_list); + +static struct conf_section * config_add_section(const char *name) +{ + struct conf_section *section; + section = malloc(sizeof(struct conf_section) + strlen(name)); + if (section == NULL) + return NULL; + + INIT_LIST_HEAD(§ion->list); + INIT_LIST_HEAD(§ion->tupel_list); + + section->name = strdup(name); + if (section->name == NULL) { + free(section); + return NULL; + } + + list_add_tail(§ion->list, &config_list); + return section; +} + +static int config_add_tupel(struct conf_section *section, const char *option, const char *parameter) +{ + struct conf_tupel *tupel = malloc(sizeof(struct conf_tupel)); + if (tupel == NULL) + return -1; + + INIT_LIST_HEAD(&tupel->list); + tupel->option = strdup(option); + tupel->parameter = strdup(parameter); + + if (tupel->option == NULL || tupel->parameter == NULL) { + free(tupel); + return -1; + } + + list_add_tail(&tupel->list, §ion->tupel_list); + return 0; +} + +int config_parse(const char *config) +{ + FILE *fz = fopen(config, "r"); + if (fz == NULL) { + log_print(LOG_ERROR, "config_parse(): %s", config); + return -1; + } + + char *line = malloc(BUFSIZE); + if (line == NULL) { + log_print(LOG_ERROR, "config_parse(): out of memory"); + fclose(fz); + return -1; + } + + int linenum = 0; + struct conf_section *section = NULL; + while (fgets(line, BUFSIZE, fz) != NULL) { + linenum++; + + if (line[0] == '#' || line[0] <= ' ') { + continue; + + } else if (line[0] == '[') { + char *tok = strtok(line +1, " ]\n"); + + if (tok == NULL || (section = config_add_section(tok)) == NULL) { + log_print(LOG_WARN, "config_parse(): invalid section in row %d", linenum); + free(line); + fclose(fz); + return -1; + } + continue; + + } else if (section == NULL) { + log_print(LOG_WARN, "config_parse(): missing section in row %d", linenum); + free(line); + fclose(fz); + return -1; + } + + char *tok = strtok(line, " \n"); + if (tok != NULL) { + char *tok2; + while ((tok2 = strtok(NULL, " \n"))) { + if (config_add_tupel(section, tok, tok2) != 0) + log_print(LOG_WARN, "config_parse(): invalid row %d", linenum); + } + } + } + + fclose(fz); + free(line); + return 0; +} + +static struct conf_section * config_get_section(const char *name) +{ + struct conf_section *section; + + list_for_each_entry(section, &config_list, list) { + if (!strcmp(section->name, name)) + return section; + } + return NULL; +} + +char * config_get_string(const char *section_str, const char *option, char *def) +{ + struct conf_section *section = config_get_section(section_str); + if (section != NULL) { + struct conf_tupel *tupel; + list_for_each_entry(tupel, §ion->tupel_list, list) { + if (!strcmp(tupel->option, option)) + return tupel->parameter; + } + } + + if (def != NULL) + log_print(LOG_WARN, "config [%s:%s] not found, using default: '%s'", + section_str, option, def); + return def; +} + +int config_get_int(const char *section, const char *option, int def) +{ + char *ret = config_get_string(section, option, NULL); + if (ret == NULL) { + log_print(LOG_WARN, "config [%s:%s] not found, using default: '%d'", + section, option, def); + return def; + } + return atoi(ret); +} + +int config_get_strings(const char *section_str, const char *option, + int (*callback)(const char *value, void *privdata), + void *privdata) +{ + struct conf_section *section = config_get_section(section_str); + if (section == NULL) + return -1; + + int cnt = 0; + struct conf_tupel *tupel; + list_for_each_entry(tupel, §ion->tupel_list, list) { + if (!strcmp(tupel->option, option)) + if (callback(tupel->parameter, privdata) == 0) + cnt++; + } + return cnt; +} + +// TODO: wrong place! +int parse_saddr(const char *addr, struct sockaddr_in *sa) +{ + char *addr_cpy = strdup(addr); + if (addr_cpy == NULL) { + log_print(LOG_WARN, "parse_saddr(): out of memory"); + return -1; + } + + char *tmp; + char *ip = strtok_r(addr_cpy, ":", &tmp); + if (ip == NULL) { + free(addr_cpy); + return -1; + } + + char *port = strtok_r(NULL, ":", &tmp); + if (port == NULL) { + free(addr_cpy); + return -1; + } + + sa->sin_family = AF_INET; + sa->sin_port = htons(atoi(port)); + int ret = inet_aton(ip, &sa->sin_addr); + + free(addr_cpy); + return (ret != 0) ? 0 : -1; +} diff --git a/configfile.h b/configfile.h new file mode 100644 index 0000000..565a8a5 --- /dev/null +++ b/configfile.h @@ -0,0 +1,18 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include + +int config_parse(const char *config); + +char * config_get_string(const char *section_str, const char *option, char *def); + +int config_get_int(const char *section, const char *option, int def); + +int config_get_strings(const char *section_str, const char *option, + int (*callback)(const char *value, void *privdata), + void *privdata); + +int parse_saddr(const char *addr, struct sockaddr_in *sa); + +#endif /* _CONFIG_H_ */ diff --git a/event.c b/event.c new file mode 100644 index 0000000..2d34826 --- /dev/null +++ b/event.c @@ -0,0 +1,310 @@ +/*************************************************************************** + * Copyright (C) 10/2006 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 "list.h" +#include "logging.h" + +#include "event.h" + +static LIST_HEAD(readfd_list); +static LIST_HEAD(writefd_list); +static LIST_HEAD(exceptfd_list); +static LIST_HEAD(timeout_list); + +struct fd_entry { + struct list_head list; + int fd; + int type; + int (*callback)(int fd, void *privdata); + void *privdata; +}; + +struct timeout_entry { + struct list_head list; + struct timeval timeout; + struct timeval nextrun; + int (*callback)(void *privdata); + void *privdata; +}; + +int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void *privdata) +{ + if (fd < 0 || fd > FD_SETSIZE) + return -1; + + struct fd_entry *entry; + entry = malloc(sizeof(struct fd_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "event_add_fd(): out of memory"); + return -1; + } + + entry->fd = fd; + entry->type = type; + entry->callback = callback; + entry->privdata = privdata; + + switch (type) { + case FD_READ: + list_add_tail(&entry->list, &readfd_list); + break; + + case FD_WRITE: + list_add_tail(&entry->list, &writefd_list); + break; + + case FD_EXCEPT: + list_add_tail(&entry->list, &exceptfd_list); + break; + + default: + log_print(LOG_ERROR, "add_fd(): unknown type"); + free(entry); + return -1; + } + + return 0; +} + +int event_remove_fd(int fd) +{ + struct fd_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &readfd_list, list) { + if (entry->fd == fd) { + list_del(&entry->list); + free(entry); + return 0; + } + } + + list_for_each_entry_safe(entry, tmp, &writefd_list, list) { + if (entry->fd == fd) { + list_del(&entry->list); + free(entry); + return 0; + } + } + + list_for_each_entry_safe(entry, tmp, &exceptfd_list, list) { + if (entry->fd == fd) { + list_del(&entry->list); + free(entry); + return 0; + } + } + + return -1; +} + +static void calc_nextrun(struct timeval *timeout, struct timeval *nextrun) +{ + struct timeval now; + gettimeofday(&now, NULL); + + nextrun->tv_usec = now.tv_usec + timeout->tv_usec; + nextrun->tv_sec = now.tv_sec + timeout->tv_sec; + + if (nextrun->tv_usec >= 1000000) { + nextrun->tv_usec -= 1000000; + nextrun->tv_sec++; + } +} + +static void calc_timeout(struct timeval *timeout, struct timeval *nextrun) +{ + struct timeval now; + gettimeofday(&now, NULL); + + timeout->tv_usec = nextrun->tv_usec - now.tv_usec; + timeout->tv_sec = nextrun->tv_sec - now.tv_sec; + + if (timeout->tv_usec < 0) { + timeout->tv_usec += 1000000; + timeout->tv_sec--; + } +} + +static void schedule_nextrun(struct timeout_entry *entry) +{ + struct timeout_entry *search; + + list_for_each_entry(search, &timeout_list, list) { + if (search->nextrun.tv_sec > entry->nextrun.tv_sec) { + list_add_tail(&entry->list, &search->list); + return; + + } else if (search->nextrun.tv_sec == entry->nextrun.tv_sec && + search->nextrun.tv_usec > entry->nextrun.tv_usec) { + list_add_tail(&entry->list, &search->list); + return; + } + } + list_add_tail(&entry->list, &timeout_list); +} + +int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), void *privdata) +{ + struct timeout_entry *entry; + entry = malloc(sizeof(struct timeout_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "event_add_timeout(): out of memory"); + return -1; + } + + memcpy(&entry->timeout, timeout, sizeof(entry->timeout)); + entry->callback = callback; + entry->privdata = privdata; + + calc_nextrun(&entry->timeout, &entry->nextrun); + schedule_nextrun(entry); + return 0; +} + +void event_loop(void) +{ + while (1) { + fd_set readfds, *readfds_p = NULL; + fd_set writefds, *writefds_p = NULL; + fd_set exceptfds, *exceptfds_p =NULL; + struct timeval timeout, *timeout_p = NULL; + + if (!list_empty(&readfd_list)) { + struct fd_entry *entry; + + FD_ZERO(&readfds); + list_for_each_entry(entry, &readfd_list, list) + FD_SET(entry->fd, &readfds); + + readfds_p = &readfds; + } + + if (!list_empty(&writefd_list)) { + struct fd_entry *entry; + + FD_ZERO(&writefds); + list_for_each_entry(entry, &writefd_list, list) + FD_SET(entry->fd, &writefds); + + writefds_p = &writefds; + } + + if (!list_empty(&exceptfd_list)) { + struct fd_entry *entry; + + FD_ZERO(&exceptfds); + list_for_each_entry(entry, &exceptfd_list, list) + FD_SET(entry->fd, &exceptfds); + + exceptfds_p = &exceptfds; + } + + if (!list_empty(&timeout_list)) { + struct timeout_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &timeout_list, list) { + + calc_timeout(&timeout, &entry->nextrun); + if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) { + timeout_p = &timeout; + break; + } + + // delayed timeout, exec NOW! + list_del(&entry->list); + int ret = entry->callback(entry->privdata); + if (ret == 0) { + calc_nextrun(&entry->timeout, &entry->nextrun); + schedule_nextrun(entry); + + } else { + free(entry); + } + } + } + + int i = select(FD_SETSIZE, readfds_p, writefds_p, exceptfds_p, timeout_p); + if (i < 0) { + /* On error, -1 is returned, and errno is set + * appropriately; the sets and timeout become + * undefined, so do not rely on their contents + * after an error. + */ + continue; + + } else if (i == 0 && !list_empty(&timeout_list)) { + struct timeout_entry *entry; + entry = list_entry(timeout_list.next, typeof(*entry), list); + list_del(&entry->list); + + int ret = entry->callback(entry->privdata); + if (ret == 0) { + calc_nextrun(&entry->timeout, &entry->nextrun); + schedule_nextrun(entry); + + } else { + free(entry); + } + } + + if (readfds_p) { + struct fd_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &readfd_list, list) { + if (!FD_ISSET(entry->fd, &readfds)) + continue; + + if (entry->callback(entry->fd, entry->privdata) != 0) { + list_del(&entry->list); + free(entry); + } + } + } + + if (writefds_p) { + struct fd_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &writefd_list, list) { + if (FD_ISSET(entry->fd, &writefds)) + continue; + + if (entry->callback(entry->fd, entry->privdata) != 0) { + list_del(&entry->list); + free(entry); + } + } + } + + if (exceptfds_p) { + struct fd_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &exceptfd_list, list) { + if (FD_ISSET(entry->fd, &exceptfds)) + continue; + + if (entry->callback(entry->fd, entry->privdata) != 0) { + list_del(&entry->list); + free(entry); + } + } + } + } +} diff --git a/event.h b/event.h new file mode 100644 index 0000000..df7a396 --- /dev/null +++ b/event.h @@ -0,0 +1,26 @@ +#ifndef _EVENT_H_ +#define _EVENT_H_ + +#include + +#define FD_READ 0x01 +#define FD_WRITE 0x02 +#define FD_EXCEPT 0x04 + +#define event_add_readfd(fd, callback, privdata) \ + event_add_fd(fd, FD_READ, callback, privdata) + +#define event_add_writefd(fd, callback, privdata) \ + event_add_fd(fd, FD_WRITE, callback, privdata) + +#define event_add_exceptfd(fd, callback, privdata) \ + event_add_fd(fd, FD_EXCEPT, callback, privdata) + +int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void *privdata); +int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), void *privdata); + +int event_remove_fd(int fd); + +void event_loop(void); + +#endif /* _EVENT_H_ */ diff --git a/gamelist.c b/gamelist.c new file mode 100644 index 0000000..c26533a --- /dev/null +++ b/gamelist.c @@ -0,0 +1,85 @@ +#include +#include +#include + +#include + +#include "gamelist.h" +#include "logging.h" + +#define BUCKET_NUM 127 + +static struct list_head *bucket; + +static unsigned int hash_entry(struct game_entry *entry) +{ + unsigned int hash = 0x12345678; + + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->ip >> 0) & 0xFF); + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->ip >> 8) & 0xFF); + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->ip >> 16) & 0xFF); + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->ip >> 24) & 0xFF); + + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->port1 >> 0) & 0xFF); + hash = ((hash << 5) ^ (hash >> 27)) ^ ((entry->port1 >> 8) & 0xFF); + + return hash % BUCKET_NUM; +} + +static int compare_entry(struct game_entry *a, struct game_entry *b) +{ + return (a->gameid == b->gameid && a->ip == b->ip && + a->port1 == b->port1 && a->port2 == b->port2); +} + +int gamelist_add(struct game_entry *entry) +{ + unsigned int hash = hash_entry(entry); + + struct game_entry *search; + list_for_each_entry(search, &bucket[hash], list) { + if (compare_entry(search, entry)) { + search->modtime = time(NULL); + return 0; + } + } + + entry->modtime = time(NULL); + list_add_tail(&entry->list, &bucket[hash]); + return 0; +} + +int gamelist_gc_and_dump(int (*callback)(struct game_entry *entry), int timeout) +{ + long now = time(NULL); + int i; + + for (i = 0; i < BUCKET_NUM; i++) { + struct game_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &bucket[i], list) { + if (entry->modtime + timeout >= now) { + callback(entry); + + } else { + list_del(&entry->list); + free(entry); + } + } + } + return 0; +} + +int gamelist_init() +{ + bucket = malloc(sizeof(struct list_head) * BUCKET_NUM); + if (bucket == NULL) { + log_print(LOG_ERROR, "scan_init(): out of memory"); + return -1; + } + + int i; + for (i = 0; i < BUCKET_NUM; i++) + INIT_LIST_HEAD(&bucket[i]); + + return 0; +} diff --git a/gamelist.h b/gamelist.h new file mode 100644 index 0000000..4c4db6f --- /dev/null +++ b/gamelist.h @@ -0,0 +1,25 @@ +#ifndef _GAMELIST_H_ +#define _GAMELIST_H_ + +#include + +#include "list.h" + +struct game_entry { + struct list_head list; + unsigned long modtime; + + /* begin HLSW_ENTRY */ + uint16_t gameid; + uint32_t ip; + uint16_t port1; + uint16_t port2; + /* end HLSW_ENTRY */ + +} __attribute__ ((packed)); + +int gamelist_init(); +int gamelist_add(struct game_entry *entry); +int gamelist_gc_and_dump(int (*callback)(struct game_entry *entry), int timeout); + +#endif /* _GAMELIST_H_ */ diff --git a/main.c b/hlswmaster.c similarity index 79% rename from main.c rename to hlswmaster.c index 693ee0f..4b68382 100644 --- a/main.c +++ b/hlswmaster.c @@ -21,13 +21,18 @@ #include #include #include -#include #include #include #include -#include "hlswmaster.h" #include "configfile.h" +#include "event.h" +#include "logging.h" + +#include "gamelist.h" +#include "scanner.h" +#include "server.h" +#include "plugin.h" static struct option opts[] = { {"config", 1, 0, 'c'}, @@ -39,8 +44,6 @@ static struct option opts[] = { int main(int argc, char *argv[]) { - pthread_t thread1, thread2, thread3; - pthread_attr_t attr; int arg = 0, code = 0, debug = 0; char *config = NULL, *user = NULL, *logfile; @@ -84,22 +87,22 @@ int main(int argc, char *argv[]) if (user) { struct passwd *pwl; if (!(pwl = getpwnam(user))) { - log_print("unknown user: %s", user); + log_print(LOG_ERROR, "unknown user: %s", user); exit(-1); } if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) { - log_print("setgid/setuid"); + log_print(LOG_ERROR, "setgid/setuid"); exit(-1); } } /* parse config file */ - if (!config_parse(config)) + if (config_parse(config) == -1) exit(-1); /* check logfile */ - logfile = config_get_string("global", "logfile", NULL); + logfile = config_get_string("global", "logfile", "hlswmaster.log"); if (logfile && !debug) { /* start logging */ if (!log_init(logfile)) @@ -109,26 +112,25 @@ int main(int argc, char *argv[]) daemon(-1, 0); } - log_print("hlswmaster started (user: %s, pid: %d)", getpwuid(getuid())->pw_name, getpid()); + log_print(LOG_INFO, "hlswmaster started (user: %s, pid: %d)", getpwuid(getuid())->pw_name, getpid()); - /* init scan engine */ - if (!scan_init()) + /* init plugins */ + if (plugin_init() == -1) exit(-1); - /* load plugins */ - plugin_load_all(); + /* init gamelist */ + if (gamelist_init() == -1) + exit(-1); - /* startup threads */ - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 65536); + /* init server */ + if (server_init() == -1) + exit(-1); - pthread_create(&thread1, &attr, (void *)&scan_control, NULL); - pthread_create(&thread2, &attr, (void *)&scan_receive, NULL); - pthread_create(&thread3, &attr, (void *)&client_handler, NULL); + /* init scanner */ + if (scanner_init() == -1) + exit(-1); - /* wait untill d00msday */ - while (1) - sleep(3600); + event_loop(); return 0; } diff --git a/hlswmaster.conf b/hlswmaster.conf index 5241c7d..d14862f 100644 --- a/hlswmaster.conf +++ b/hlswmaster.conf @@ -1,43 +1,43 @@ [global] -# broadcast scan source IP & PORT (udp) -scan_ip 0.0.0.0 -scan_port 7130 +## broadcast scan source IP & PORT (udp) +#scanner_src 0.0.0.0:7130 -# broadcast scan every X seconds -scan_interval 60 +## broadcast scan every X seconds +#scan_interval 60 -# serverlist rebuild every X seconds -serverlist_interval 5 +## serverlist rebuild every X seconds +#serverlist_refresh 5 -# server timeout after X seconds -serverlist_timeout 180 +## server timeout after X seconds +#serverlist_timeout 180 -# plugin data timeout every X seconds -plugin_timeout 90 +## master answers with this source IP +#master_src 0.0.0.0:7140 -# master answers with this source IP -master_ip 0.0.0.0 +#plugin_dir . +plugin p_d3engine.so +plugin p_gamespy1.so +plugin p_gamespy2.so +plugin p_halflife.so +plugin p_hlswproxy.so +plugin p_q3engine.so +plugin p_quake2.so +plugin p_ut2k4.so -# load these plugins -#plugin plugins/.libs/hlswproxy.so -plugin plugins/.libs/halflife.so -plugin plugins/.libs/q3engine.so -plugin plugins/.libs/quake2.so -plugin plugins/.libs/gamespy1.so -plugin plugins/.libs/gamespy2.so -plugin plugins/.libs/d3engine.so -plugin plugins/.libs/ut2k4.so +## logging +#logfile hlswmaster.log -# logging -logfile hlswmaster.log +[gamespy1] +## internal fragment-list timeout +#gc_timeout 90 [hlswproxy] -# ask these hlswmasters -scan_ip 10.10.0.1 -scan_ip 10.10.0.2 +## ask these hlswmasters +scan 10.10.0.1:7140 +scan 10.10.0.2:7140 [halflife] -# allow these nets +## allow these nets valid_net 10.10.0.0/16 valid_net 172.16.0.0/16 diff --git a/hlswmaster.h b/hlswmaster.h deleted file mode 100644 index dc9850d..0000000 --- a/hlswmaster.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _HLSWMASTER_H -#define _HLSWMASTER_H - -#include - -#include "list.h" -#include "netpkt.h" - -struct game_server { - struct list_head list; - unsigned long modtime; - - uint16_t gameid; - uint32_t ip; - uint16_t port1; - uint16_t port2; - -} __attribute__ ((packed)); - -/* logging.c */ -int log_init(char *logfile); -void log_print(const char *fmt, ... ); - -/* plugin.c */ -int plugin_load(char *name); -int plugin_load_all(); -int plugin_unload_all(); -int plugins_scan(); -int plugins_parse(struct net_pkt *pkt); -int plugins_gc(unsigned long timeout); - -/* scanner.c */ -int scan_init(); -void scan_control(); -void scan_receive(); - -/* client.c */ -int client_pkt_add(struct game_server *server); -int client_pkt_commit(); -void client_handler(); - -#endif /* _HLSWMASTER_H */ diff --git a/list.h b/list.h index facf747..61f8d93 100644 --- a/list.h +++ b/list.h @@ -1,5 +1,5 @@ -#ifndef _LIST_H -#define _LIST_H +#ifndef _LIST_H_ +#define _LIST_H_ /* * stolen from linux kernel 2.6.11 (http://kernel.org/) @@ -265,4 +265,4 @@ static inline void list_splice_init(struct list_head *list, (type)__j; \ }) -#endif /* _LIST_H */ +#endif /* _LIST_H_ */ diff --git a/logging.c b/logging.c index 1a2d6b8..d4fd706 100644 --- a/logging.c +++ b/logging.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 03/2005 by Olaf Rempel * + * Copyright (C) 06/2006 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * @@ -24,84 +24,80 @@ #include #include -#include "configfile.h" +#include "logging.h" + +#define BUFSIZE 8192 static FILE *log_fd = NULL; +static char *buffer = NULL; -/** - * log_print() - * schreibt eine Zeile ins Logfile oder auf stderr - * wenn errno gesetzt ist, wird der error-string mit ausgegeben - * - * @param fmt variable argumentenliste (syntax wie bei printf) - * - * @todo code vereinfachen? - */ -void log_print(const char *fmt, ...) +void log_print(int prio, const char *fmt, ...) { va_list az; - char *buffer = malloc(8192); + int len; - va_start(az, fmt); - vsprintf(buffer, fmt, az); - va_end(az); - - /* ins logfile loggen */ - if (log_fd) { - time_t tzgr; - char tbuf[64]; - time(&tzgr); - strftime(tbuf, 64, "%b %d %H:%M:%S :", localtime(&tzgr)); - - if (errno) { - fprintf(log_fd, "%s %s: %s\n", tbuf, buffer, strerror(errno)); - } else { - fprintf(log_fd, "%s %s\n", tbuf, buffer); - } - - fflush(log_fd); - - /* nach stderr loggen */ - } else { - if (errno) { - fprintf(stderr, "%s: %s\n", buffer, strerror(errno)); - } else { - fprintf(stderr, "%s\n", buffer); + if (buffer == NULL) { + buffer = malloc(BUFSIZE); + if (buffer == NULL) { + fprintf(stderr, "log_print: out of memory\nBailing out!\n"); + exit(-1); } } + + va_start(az, fmt); + len = vsnprintf(buffer, BUFSIZE, fmt, az); + va_end(az); + + if (len < 0 || len >= BUFSIZE) { + log_print(LOG_ERROR, "log_print: arguments too long"); + errno = 0; + return; + } + + if (errno) { + strncpy(buffer + len, ": ", BUFSIZE - len); + len += 2; + strncpy(buffer + len, strerror(errno), BUFSIZE - len); + } + + if (log_fd) { + char tbuf[64]; + time_t tzgr; + + time(&tzgr); + strftime(tbuf, sizeof(tbuf), "%b %d %H:%M:%S :", localtime(&tzgr)); + + fprintf(log_fd, "%s %s\n", tbuf, buffer); + fflush(log_fd); + + } else { + fprintf(stderr, "%s\n", buffer); + } + errno = 0; - free(buffer); } -/** - * log_close() - * schliesst das logfile - */ -static void log_close() +static void log_close(void) { + if (buffer) + free(buffer); + fclose(log_fd); } -/** - * log_init() - * initialisiert das logging - * - * @param logfile filename des logfiles - * @return false bei fehler - */ int log_init(char *logfile) { - if ((log_fd = fopen(logfile, "a" )) == NULL) { - log_print("log_open('%s'): %s", logfile); + log_fd = fopen(logfile, "a"); + if (log_fd == NULL) { + log_print(LOG_ERROR, "log_open('%s'): %s", logfile); return 0; } - /* beim beenden log schliessen */ if (atexit(log_close) != 0) { - log_print("log_open(): atexit()"); + log_print(LOG_ERROR, "log_open(): atexit()"); return 0; } - - log_print("=========================="); + + log_print(LOG_EVERYTIME, "=========================="); return 1; } diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..c6e32c0 --- /dev/null +++ b/logging.h @@ -0,0 +1,16 @@ +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#define LOG_DEBUG 5 +#define LOG_INFO 4 +#define LOG_NOTICE 3 +#define LOG_WARN 2 +#define LOG_ERROR 1 +#define LOG_CRIT 0 + +#define LOG_EVERYTIME 0 + +int log_init(char *logfile); +void log_print(int prio, const char *fmt, ... ); + +#endif /* _LOGGING_H_ */ diff --git a/masterquery.c b/masterquery.c index bf9e87d..eb56646 100644 --- a/masterquery.c +++ b/masterquery.c @@ -22,7 +22,7 @@ struct _entry { static char hlswheader[] = "\xFF\xFF\xFF\xFFHLSWLANSEARCH"; static char *id2name[] = { - "Unknown", // 0 + "Unknown", // 0 "Halflife", "Quake 1", "Quake 2", @@ -53,7 +53,7 @@ static char *id2name[] = { "Medal of Honor: Allied Assault Breakthrough", "Tribes 2", "Halo", // 30 - "Call of Duty", + "Call of Duty", "Savage: The Battle for Newerth", "Unreal Tournament 2004", "HLSteam", @@ -68,8 +68,8 @@ static char *id2name[] = { "Starwars: Battlefront (?)", "SWAT 4", "Battlefield 2", // 45 - "???", - "Quake 4 (???)", + "(unknown)", + "Quake 4 (?)", "Call of Duty 2" }; @@ -86,9 +86,9 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) } else { printf("received hlsw packet from: %15s:%-5d size=%d count=%d\n", - inet_ntoa(src->sin_addr), ntohs(src->sin_port), size, + inet_ntoa(src->sin_addr), ntohs(src->sin_port), size, ((size > sizeof(hlswheader)) ? (size - sizeof(hlswheader)) / sizeof(struct _entry) : 0)); - + if (verbose) { server = pkt + sizeof(hlswheader); while ((void *)server < pkt + size) { @@ -105,7 +105,7 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) static int scan_init() { int i = 1; - + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket()"); return -1; @@ -115,7 +115,7 @@ static int scan_init() perror("setsockopt()"); return -1; } - + return 0; } @@ -139,10 +139,10 @@ static int scan_receive() FD_ZERO(&fdsel); FD_SET(sock, &fdsel); - + tv.tv_sec = 1; tv.tv_usec = 0; - + /* timeout */ while (tv.tv_sec > 0 || tv.tv_usec > 0) { @@ -156,7 +156,7 @@ static int scan_receive() perror("ioctl()"); return -1; } - + if (recvsize > 0) { if (!(pkt = malloc(recvsize))) { perror("malloc()"); @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) exit(-1); } break; - + case 'i': /* intervall */ freq = atoi(optarg); if (freq < 1) { @@ -214,7 +214,7 @@ int main(int argc, char *argv[]) case 'v': /* verbose */ verbose = 1; break; - + case 'h': /* help */ printf("Usage: masterquery [options]\n" "Options: \n" diff --git a/netpkt.c b/netpkt.c new file mode 100644 index 0000000..4fceaf2 --- /dev/null +++ b/netpkt.c @@ -0,0 +1,143 @@ +#include + +#define __USE_GNU +#include + +#include + +#include +#include +#include +#include + +#include "netpkt.h" + +int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) +{ + + if (offset >= pkt->size) + return 1; + + /* nicht ueber das paket hinaus vergleichen */ + if (offset + size >= pkt->size) + size = pkt->size - offset; + + return memcmp(pkt->buf + offset, search, size); +} + +int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) +{ + void *found; + + if (offset >= pkt->size) + return -1; + + found = memmem(pkt->buf + offset, pkt->size, search, size); + + return (found == NULL) ? -1 : (found - (void *)pkt->buf); +} + +struct net_pkt * pkt_merge(struct net_pkt *pkt1, struct net_pkt *pkt2) +{ + struct net_pkt *ret; + ret = malloc(sizeof(struct net_pkt) + pkt1->size + pkt2->size); + + memcpy(&ret->addr, &pkt1->addr, sizeof(ret->addr)); + ret->size = pkt1->size + pkt2->size; + + memcpy(ret->buf, pkt1->buf, pkt1->size); + memcpy(ret->buf + pkt1->size, pkt2->buf, pkt2->size); + + return ret; +} + +char * pkt_ntoa(struct net_pkt *pkt) +{ + return inet_ntoa(pkt->addr.sin_addr); +} + +unsigned short pkt_getport(struct net_pkt *pkt) +{ + return ntohs(pkt->addr.sin_port); +} + +int pkt_sameaddr(struct net_pkt *pkt1, struct net_pkt *pkt2) +{ + return (pkt1->addr.sin_addr.s_addr == pkt2->addr.sin_addr.s_addr) && + (pkt1->addr.sin_port == pkt2->addr.sin_port); +} + +int pkt_parse_int(struct net_pkt *pkt, unsigned int offset, int *val) +{ + unsigned char *max = pkt->buf + pkt->size; + unsigned char *c = pkt->buf + offset; + + /* untere grenze abtesten */ + if (pkt->buf > c || c > max) + return -1; + + *val = 0; + + /* ziffern einlesen */ + while (isdigit(*c) && c < max) + *val = (*val * 10) + (*c++ - 0x30); + + return (c - (pkt->buf + offset)); +} + +int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip) +{ + int i, tmp, count, pos = offset; + ip->s_addr = 0; + + for (i = 0; i < 4; i++) { + count = pkt_parse_int(pkt, pos, &tmp); + pos += count; + if (count == 0 || tmp < 0 || tmp > 255) + return 0; + + ip->s_addr = ip->s_addr>>8 | tmp<<24; + + if (i != 3 && pkt->buf[pos++] != '.') + return 0; + } + return pos - offset; +} + +char * pkt_print(struct net_pkt *pkt) +{ + int pos = 0, i = 0, j; + char *buf = malloc(pkt->size * 4 + 64); + + while (pos < pkt->size) { + i += sprintf(buf + i, "%04X: ", pos); + for (j = 0; j < 16; j++) { + if (pos + j < pkt->size) + i += sprintf(buf + i, "%02X", pkt->buf[pos + j]); + else + i += sprintf(buf + i, " "); + + if (j % 2) + buf[i++] = ' '; + } + + for (j = 0; j < 16; j++) { + if (pos + j < pkt->size) { + unsigned char val = pkt->buf[pos + j]; + if (val >= 0x20 && val < 0x80) + buf[i++] = val; + else + buf[i++] = '.'; + } else { + buf[i++] = ' '; + } + } + + pos += 16; + buf[i++] = '\r'; + buf[i++] = '\n'; + } + buf[i] = 0; + return buf; +} + diff --git a/netpkt.h b/netpkt.h index 0b42ad8..1e05313 100644 --- a/netpkt.h +++ b/netpkt.h @@ -5,10 +5,24 @@ #include #include +#include "list.h" + struct net_pkt { + struct list_head list; struct sockaddr_in addr; unsigned int size; unsigned char buf[0]; }; +int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); +int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); +struct net_pkt * pkt_merge(struct net_pkt *pkt1, struct net_pkt *pkt2); + +char * pkt_ntoa(struct net_pkt *pkt); +unsigned short pkt_getport(struct net_pkt *pkt); +int pkt_sameaddr(struct net_pkt *pkt1, struct net_pkt *pkt2); +int pkt_parse_int(struct net_pkt *pkt, unsigned int offset, int *val); +int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip); +char * pkt_print(struct net_pkt *pkt); + #endif /* _NETPKT_H */ diff --git a/p_d3engine.c b/p_d3engine.c index 7dfc2de..451a7b7 100644 --- a/p_d3engine.c +++ b/p_d3engine.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include +#include "netpkt.h" +#include "plugin.h" #include "plugin_helper.h" static struct scan_ports port_arr[] = { @@ -41,7 +43,7 @@ static int parse(struct net_pkt *pkt) if (!(gameid = pkt_check_portarr(pkt, port_arr))) return PARSE_REJECT; - + if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) return PARSE_REJECT; diff --git a/p_gamespy1.c b/p_gamespy1.c index b40695a..571d809 100644 --- a/p_gamespy1.c +++ b/p_gamespy1.c @@ -17,7 +17,14 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include #include +#include + +#include "logging.h" +#include "event.h" +#include "netpkt.h" +#include "plugin.h" #include "plugin_helper.h" static struct scan_ports port_arr[] = { @@ -71,7 +78,7 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid, struct net_pkt *retpkt = NULL; struct gs1_part *part, *tmp; unsigned int i = 1, found = 0, notfound = 0; - + // wenn paket non-final ist, in liste speichern // NULL zurueckgeben if (pkt_memmem(pkt, 0, search_final, strlen(search_final)) == -1) { @@ -112,7 +119,7 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid, /* merge error, eat last paket, cleanup */ if (i != subid || notfound > subid) { - log_print("gs1: merging error!"); + log_print(LOG_INFO, "gs1: merging error!"); free(pkt); return NULL; } @@ -123,12 +130,12 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid, static int parse_real(struct net_pkt *pkt, int gameid) { int port, offset, pos1, pos2; - + pos1 = pkt_memmem(pkt, 0, search_gamename, strlen(search_gamename)); pos1 += strlen(search_gamename); pos2 = pkt_memmem(pkt, 0, search_gameid, strlen(search_gameid)); pos2 += strlen(search_gameid); - + switch (gameid) { case 5:/* unreal tournament */ if (!pkt_memcmp(pkt, pos1, reply_ut, strlen(reply_ut))) @@ -141,7 +148,7 @@ static int parse_real(struct net_pkt *pkt, int gameid) /* unreal tournament 2k4 */ else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4))) gameid = 33; - + /* americas army operations */ else if (!pkt_memcmp(pkt, pos1, reply_aao, strlen(reply_aao))) gameid = 15; @@ -156,10 +163,10 @@ static int parse_real(struct net_pkt *pkt, int gameid) else if (!pkt_memcmp(pkt, pos2, reply_bfv1, strlen(reply_bfv1))) gameid = 35; - + else if (!pkt_memcmp(pkt, pos2, reply_bfv2, strlen(reply_bfv2))) gameid = 35; - + else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe))) gameid = 35; @@ -216,7 +223,7 @@ static int parse(struct net_pkt *pkt) { struct net_pkt *pkt2; int gameid, pos, offset, queryid, subid, retval; - + if (!(gameid = pkt_check_portarr(pkt, port_arr))) return PARSE_REJECT; @@ -226,15 +233,15 @@ static int parse(struct net_pkt *pkt) if ((offset = pkt_memmem(pkt, 0, search_queryid, strlen(search_queryid))) == -1) return PARSE_REJECT; - + pos = offset + strlen(search_queryid); if ((offset = pkt_parse_int(pkt, pos, &queryid)) == 0) return PARSE_REJECT; - + pos += offset +1; if ((offset = pkt_parse_int(pkt, pos, &subid)) == 0) return PARSE_REJECT; - + /* multipaket antworten zusammenfassen * wenn paket non-final, dann einfach annehmen */ @@ -242,7 +249,7 @@ static int parse(struct net_pkt *pkt) return PARSE_ACCEPT_FREED; retval = parse_real(pkt2, gameid); - + /* free merged packet */ if (pkt != pkt2) free(pkt2); @@ -250,23 +257,38 @@ static int parse(struct net_pkt *pkt) return retval; } -static int gc(int timeout) { - struct gs1_part *part, *tmp; +static int gc(void *privdata) { + + unsigned int timeout = (int)privdata; unsigned long now = time(NULL); - + + struct gs1_part *part, *tmp; list_for_each_entry_safe(part, tmp, &gs1_partlist, list) { if (part->timeout + timeout < now) { - log_print("gs1: removing dead fragment"); + log_print(LOG_INFO, "gs1: removing dead fragment"); list_del(&part->list); free(part->pkt); free(part); } } + return 0; +} + +static int init(void) +{ + int timeout = config_get_int("gamespy1", "gc_timeout", 90); + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + event_add_timeout(&tv, gc, (void *)timeout); + + return 0; } struct hlswmaster_plugin plugin = { .name = "gamespy1", .scan = &scan, .parse = &parse, - .gc = &gc, + .init = &init, }; diff --git a/p_gamespy2.c b/p_gamespy2.c index 9994063..a335151 100644 --- a/p_gamespy2.c +++ b/p_gamespy2.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include +#include "netpkt.h" +#include "plugin.h" #include "plugin_helper.h" static struct scan_ports port_arr[] = { @@ -48,7 +50,7 @@ static int parse(struct net_pkt *pkt) if (!(gameid = pkt_check_portarr(pkt, port_arr))) return PARSE_REJECT; - + if (pkt_memcmp(pkt, 0, replymsg, sizeof(replymsg))) return PARSE_REJECT; @@ -61,7 +63,7 @@ static int parse(struct net_pkt *pkt) pkt_parse_int(pkt, pos3 + strlen(search_hostport) +1, &port); switch (gameid) { - case 30:/* halo */ + case 30:/* halo */ case 37:/* painkiller */ break; @@ -71,7 +73,7 @@ static int parse(struct net_pkt *pkt) else return PARSE_REJECT; break; - + case 45:/* battlefield 2 */ // todo: pos3 check noetig? if (!pkt_memcmp(pkt, pos1, reply_bf2, strlen(reply_bf2)) && pos3 != -1) @@ -79,12 +81,12 @@ static int parse(struct net_pkt *pkt) else return PARSE_REJECT; break; - + default: return PARSE_REJECT; break; } - + /* * wenn ein hostport angegeben wurde, und das nicht der src port ist * beide ports in die serverliste uebernehmen @@ -94,7 +96,7 @@ static int parse(struct net_pkt *pkt) } else { server_add_pkt(gameid, pkt); } - + return PARSE_ACCEPT; } diff --git a/p_halflife.c b/p_halflife.c index af2c6dc..112cf3e 100644 --- a/p_halflife.c +++ b/p_halflife.c @@ -17,16 +17,25 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include #include -#include "plugin_helper.h" -struct net_entry { +#include +#include +#include + +#include "netpkt.h" +#include "plugin.h" +#include "plugin_helper.h" +#include "logging.h" + +struct validnet_entry { + struct list_head list; struct in_addr ip; - struct in_addr mask; + struct in_addr netmask; }; -static struct net_entry *net_arr = NULL; -static int net_arr_size = 0; +static LIST_HEAD(validnet_list); static struct scan_ports port_arr[] = { { 27015, 27024, 1 }, /* cs/hl */ @@ -48,7 +57,7 @@ static int scan(void) static int parse(struct net_pkt *pkt) { struct in_addr tmp; - int port, count, pos, i; + int port, count, pos; if (!pkt_check_portarr(pkt, port_arr)) return PARSE_REJECT; @@ -62,13 +71,13 @@ static int parse(struct net_pkt *pkt) server_add_pkt(1, pkt); return PARSE_ACCEPT; } - + /* second query?! */ if (pkt->size >= 5 && pkt->buf[4] == 'I' && pkt->buf[5] == 0x07) { server_add_pkt(40, pkt); return PARSE_ACCEPT; } - + /* parse server IP */ pos = 5; if ((count = pkt_parse_ip(pkt, pos, &tmp)) == 0) @@ -80,8 +89,9 @@ static int parse(struct net_pkt *pkt) return PARSE_REJECT; /* check server IP */ - for (i = 0; i < net_arr_size; i++) { - if (((net_arr[i].ip.s_addr ^ tmp.s_addr) & net_arr[i].mask.s_addr) == 0) { + struct validnet_entry *entry; + list_for_each_entry(entry, &validnet_list, list) { + if (((entry->ip.s_addr ^ tmp.s_addr) & entry->netmask.s_addr) == 0) { server_add(1, tmp.s_addr, port, 0); return PARSE_ACCEPT; } @@ -91,86 +101,74 @@ static int parse(struct net_pkt *pkt) return PARSE_ACCEPT; } -static int parse_net(char *buf, struct net_entry *net) +// TODO: wrong place +int parse_ipmask(const char *value, struct in_addr *ip, struct in_addr *netmask) { - char *p; - + + char *buf = strdup(value); + if (buf == NULL) + return -1; + + int retval = -1; + char *p = strchr(buf, '/'); + /* valid_net x.x.x.x */ - if ((p = strchr(buf, '/')) == NULL) { - /* ip */ - if (inet_pton(AF_INET, buf, &net->ip) <= 0) - return 0; - - /* mask */ - net->mask.s_addr = 0xFFFFFFFF; - return 1; - - /* valid_net x.x.x.x/x.x.x.x oder x.x.x.x/xx */ - } else { - int retval, mask; - *p = 0x00; - - /* ip */ - if (inet_pton(AF_INET, buf, &net->ip) <= 0) { + if (p == NULL) { + /* single ip */ + if (inet_pton(AF_INET, buf, ip) > 0) { + netmask->s_addr = 0xFFFFFFFF; retval = 0; + } - /* x.x.x.x/x.x.x.x */ - } else if (inet_pton(AF_INET, p+1, &net->mask) > 0) { - retval = 1; - - /* x.x.x.x/xx */ - } else { - mask = atoi(p+1); - - if (mask >= 0 && mask <= 32) { - net->mask.s_addr = htonl(0xFFFFFFFF << (32 - mask)); - retval = 1; + /* valid_net x.x.x.x/x.x.x.x or x.x.x.x/xx */ + } else { + *p = 0x00; - } else { + /* ip */ + if (inet_pton(AF_INET, buf, &ip) > 0) { + + /* x.x.x.x/x.x.x.x */ + if (inet_pton(AF_INET, p+1, netmask) > 0) { retval = 0; + + /* x.x.x.x/xx */ + } else { + int mask = atoi(p+1); + + if (mask >= 0 && mask <= 32) { + netmask->s_addr = htonl(0xFFFFFFFF << (32 - mask)); + retval = 0; + } } } - *p = '/'; - return retval; } + free(buf); + return retval; } -static int init(struct conf_section *section) +static int init_callback(const char *value, void *privdata) { - struct conf_tupel *tupel; - static char buf[32]; - int i = 0, j; - - list_for_each_entry(tupel, §ion->tupel, list) - if (!strcmp(tupel->option, "valid_net")) - i++; - - if (i == 0 || !(net_arr = malloc(sizeof(struct net_entry) * i))) - return 0; - - i = 0; - list_for_each_entry(tupel, §ion->tupel, list) { - if (strcmp(tupel->option, "valid_net")) - continue; - - if (parse_net(tupel->parameter, &net_arr[i])) { - j = sprintf(buf, " adding: %s", inet_ntoa(net_arr[i].ip)); - sprintf(buf + j, "/%s", inet_ntoa(net_arr[i].mask)); - log_print(buf); - i++; - } else { - log_print(" invalid net: %s", tupel->parameter); - } + struct validnet_entry *entry = malloc(sizeof(struct validnet_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "halflife_init(): out of memory"); + return -1; } - net_arr_size = i; - return 1; + + if (parse_ipmask(value, &entry->ip, &entry->netmask) != 0) { + log_print(LOG_WARN, " invalid dst: %s", value); + free(entry); + return -1; + } + + list_add(&entry->list, &validnet_list); + return 0; } -static int fini() +static int init(void) { - if (net_arr_size > 0 && net_arr) - free(net_arr); + config_get_strings("halflife", "valid_net", init_callback, NULL); + return 0; } struct hlswmaster_plugin plugin = { @@ -178,5 +176,4 @@ struct hlswmaster_plugin plugin = { .scan = &scan, .parse = &parse, .init = &init, - .fini = &fini, }; diff --git a/p_hlswproxy.c b/p_hlswproxy.c index cc5b89a..b09b5c4 100644 --- a/p_hlswproxy.c +++ b/p_hlswproxy.c @@ -17,82 +17,89 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include #include -#include -#include #include -#include -#include #include -#include "plugin_helper.h" +#include +#include +#include -struct hlsw_entry { +#include "netpkt.h" +#include "plugin.h" +#include "plugin_helper.h" +#include "logging.h" +#include "scanner.h" + +struct game_entry { uint16_t gameid; uint32_t ip; uint16_t port1; uint16_t port2; } __attribute__ ((packed)); -static struct in_addr *ip_arr = NULL; -static int ip_arr_size = 0; +struct master_entry { + struct list_head list; + struct sockaddr_in addr; +}; + +static LIST_HEAD(master_list); static char scanmsg[] = "\xff\xff\xff\xffHLSWLANSEARCH"; static int scan(void) { - int i; - for (i = 0; i < ip_arr_size; i++) - pkt_send(&ip_arr[i], 7140, scanmsg, sizeof(scanmsg)); + struct master_entry *entry; + list_for_each_entry(entry, &master_list, list) + pkt_send(&entry->addr.sin_addr, ntohs(entry->addr.sin_port), scanmsg, sizeof(scanmsg)); + return 1; } static int parse(struct net_pkt *pkt) { - struct hlsw_entry *server; + // TODO: check against master_list if (pkt_getport(pkt) != 7140) return PARSE_REJECT; - server = (void *)pkt->buf + sizeof(scanmsg); + struct game_entry *game = (void *)pkt->buf + sizeof(scanmsg); - while ((void *)server < ((void *)pkt->buf + pkt->size)) { - server_add(server->gameid, server->ip, server->port1, server->port2); - server++; + while ((void *)game < ((void *)pkt->buf + pkt->size)) { + server_add(game->gameid, game->ip, game->port1, game->port2); + game++; } - + return PARSE_ACCEPT; } -static int init(struct conf_section *section) +static int init_callback(const char *value, void *privdata) { - struct conf_tupel *tupel; - int i = 0; - list_for_each_entry(tupel, §ion->tupel, list) - if (!strcmp(tupel->option, "scan_ip")) - i++; - - if (i == 0 || !(ip_arr = malloc(sizeof(struct in_addr) * i))) - return 0; - - i = 0; - list_for_each_entry(tupel, §ion->tupel, list) { - if (!strcmp(tupel->option, "scan_ip")) { - if (inet_pton(AF_INET, tupel->parameter, &ip_arr[i]) <= 0) { - log_print(" invalid ip: %s", tupel->parameter); - } else { - log_print(" adding %s", tupel->parameter); - i++; - } - } + struct master_entry *entry = malloc(sizeof(struct master_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "hlswproxy_init(): out of memory"); + return -1; } - ip_arr_size = i; - return 1; + + if (parse_saddr(value, &entry->addr) != 0) { + log_print(LOG_WARN, " invalid dst: %s", value); + free(entry); + return -1; + } + + log_print(LOG_INFO, " adding remote master %s:%d", + inet_ntoa(entry->addr.sin_addr), ntohs(entry->addr.sin_port)); + + list_add(&entry->list, &master_list); + return 0; } -static int fini() +static int init(void) { - if (ip_arr_size > 0 && ip_arr) - free(ip_arr); + if (config_get_strings("hlswproxy", "scan", init_callback, NULL) <= 0) + return -1; + + return 0; } struct hlswmaster_plugin plugin = { @@ -100,5 +107,4 @@ struct hlswmaster_plugin plugin = { .scan = &scan, .parse = &parse, .init = &init, - .fini = &fini, }; diff --git a/p_q3engine.c b/p_q3engine.c index b4353a9..12c5762 100644 --- a/p_q3engine.c +++ b/p_q3engine.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include +#include "netpkt.h" +#include "plugin.h" #include "plugin_helper.h" static struct scan_ports port_arr[] = { @@ -51,7 +53,7 @@ static int scan(void) static int parse(struct net_pkt *pkt) { int gameid = 0, pos1, pos2; - + if (!pkt_check_portarr(pkt, port_arr)) return PARSE_REJECT; @@ -65,38 +67,38 @@ static int parse(struct net_pkt *pkt) pos1 += strlen(search_version); if (!pkt_memcmp(pkt, pos1, reply_q3, strlen(reply_q3))) gameid = 6; - + else if (!pkt_memcmp(pkt, pos1, reply_ef, strlen(reply_ef))) gameid = 7; - + else if (!pkt_memcmp(pkt, pos1, reply_rtcw, strlen(reply_rtcw))) gameid = 8; else if (!pkt_memcmp(pkt, pos1, reply_et, strlen(reply_et))) gameid = 25; - + else if (!pkt_memcmp(pkt, pos1, reply_jk2, strlen(reply_jk2))) gameid = 12; else if (!pkt_memcmp(pkt, pos1, reply_jk3, strlen(reply_jk3))) gameid = 27; } - + if (gameid == 0 && pos2 != -1) { pos2 += strlen(search_gamename); if (!pkt_memcmp(pkt, pos2, reply_cod, strlen(reply_cod))) gameid = 31; - + else if (!pkt_memcmp(pkt, pos2, reply_coduo, strlen(reply_coduo))) gameid = 42; else if (!pkt_memcmp(pkt, pos2, reply_cod2, strlen(reply_cod2))) gameid = 48; } - + if (gameid == 0) return PARSE_REJECT; - + server_add_pkt(gameid, pkt); return PARSE_ACCEPT; } diff --git a/p_quake2.c b/p_quake2.c index 008055b..920391d 100644 --- a/p_quake2.c +++ b/p_quake2.c @@ -18,7 +18,10 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include +#include "netpkt.h" +#include "plugin.h" #include "plugin_helper.h" +#include "scanner.h" static char scanmsg[] = "\xff\xff\xff\xffinfo 34"; /* q2(3) */ static char replymsg[] = "\xff\xff\xff\xffinfo"; @@ -33,7 +36,7 @@ static int parse(struct net_pkt *pkt) { if (pkt_getport(pkt) != 27910) return PARSE_REJECT; - + if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) return PARSE_REJECT; diff --git a/p_ut2k4.c b/p_ut2k4.c index cd6fe71..ef22d58 100644 --- a/p_ut2k4.c +++ b/p_ut2k4.c @@ -18,6 +18,9 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include +#include "netpkt.h" +#include "scanner.h" +#include "plugin.h" #include "plugin_helper.h" static char scanmsg1[] = "\x80\x00\x00\x00\x00"; diff --git a/plugin.c b/plugin.c index b7b76ae..0ce8328 100644 --- a/plugin.c +++ b/plugin.c @@ -17,174 +17,97 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include #include +#include #include -#include -#include "hlswmaster.h" #include "plugin.h" #include "configfile.h" +#include "logging.h" #include "netpkt.h" #include "list.h" +#include "event.h" + +#define BUFSIZE 512 -/** liste der geladenen plugins */ static LIST_HEAD(plugin_list); -/** sichert die plugin liste UND die plugins selbst ab */ -static pthread_mutex_t plugin_lock = PTHREAD_MUTEX_INITIALIZER; - -/** - * plugin_load() - * laedt ein plugin und fuegt es in die plugin liste ein - * - * @param *name filename des plugins - * @return false on error - */ -int plugin_load(char *name) +static int plugin_load(const char *filename, void *privdata) { - struct hlswmaster_plugin *plugin; - void *tmp; - struct conf_section *section; + char *plugin_dir = (char *)privdata; + + char *fullname = malloc(BUFSIZE); + if (fullname == NULL) { + log_print(LOG_ERROR, "plugin_load: out of memory"); + return -1; + } + + int len = snprintf(fullname, BUFSIZE, "%s/%s", plugin_dir, filename); + if (len < 0 || len >= BUFSIZE) { + log_print(LOG_ERROR, "plugin_load: file name too long: %s/%s", plugin_dir, filename); + free(fullname); + return -1; + } dlerror(); - - /* shared objekt oeffnen */ - if ((tmp = dlopen(name, RTLD_NOW))) { - - /* plugin struct suchen */ - if ((plugin = dlsym(tmp, "plugin"))) { - plugin->dlref = tmp; - - section = config_get_section(plugin->name); - log_print("loading plugin '%s'", plugin->name); - - /* wenn vorhanden, init aufrufen */ - if (!plugin->init || (plugin->init(section))) { - pthread_mutex_lock(&plugin_lock); - list_add_tail(&plugin->list, &plugin_list); - pthread_mutex_unlock(&plugin_lock); - return 1; - } - - log_print("failed to load '%s'", name); - dlclose(tmp); - return 0; - } - dlclose(tmp); + void *dlref = dlopen(fullname, RTLD_NOW); + if (dlref == NULL) { + log_print(LOG_WARN, "plugin_load: %s", dlerror()); + free(fullname); + return -1; } - log_print("%s", dlerror()); - return 0; + + struct hlswmaster_plugin *plugin = dlsym(dlref, "plugin"); + if (plugin == NULL) { + log_print(LOG_WARN, "plugin_load: invalid plugin '%s'", fullname); + dlclose(dlref); + free(fullname); + return -1; + } + + log_print(LOG_INFO, "loading plugin '%s'", plugin->name); + + if (plugin->init == NULL || (plugin->init() == 0)) { + list_add_tail(&plugin->list, &plugin_list); + free(fullname); + return 0; + } + + log_print(LOG_WARN, "plugin_load: failed to load '%s'", plugin->name); + dlclose(dlref); + free(fullname); + return -1; } - -/** - * plugin_load_all() - * laedt alle in der config angegebenen plugins - * - * @return false on error - */ -int plugin_load_all() -{ - struct conf_section *section; - struct conf_tupel *tupel; - - section = config_get_section("global"); - if (section) { - list_for_each_entry(tupel, §ion->tupel, list) - if (!strcmp(tupel->option, "plugin")) - plugin_load(tupel->parameter); - } - return 1; -} - - -/** - * plugin_unload_all() - * entfernt alle plugins aus der liste, - * - * @return false on error - */ -int plugin_unload_all() -{ - struct hlswmaster_plugin *plugin, *tmp; - - pthread_mutex_lock(&plugin_lock); - list_for_each_entry_safe(plugin, tmp, &plugin_list, list) { - list_del(&plugin->list); - - if (plugin->fini) - plugin->fini(); - - log_print("plugin '%s' unloaded", plugin->name); - dlclose(plugin->dlref); - } - pthread_mutex_unlock(&plugin_lock); - return 1; -} - -/** - * plugins_scan() - * ruft die scan() Funktionen der Plugins auf - * - * @return true - */ int plugins_scan(void) { struct hlswmaster_plugin *plugin; - - pthread_mutex_lock(&plugin_lock); list_for_each_entry(plugin, &plugin_list, list) - /* wenn vorhanden die scan funktion aufrufen */ if (plugin->scan && !plugin->scan()) - log_print("plugin %s: scan error", plugin->name); + log_print(LOG_WARN, "plugin %s: scan error", plugin->name); - pthread_mutex_unlock(&plugin_lock); - return 1; + return 0; } -/** - * plugins_parse() - * ruft die parse() Funktionen der Plugins auf - * bis ein Plugin das Paket annimmt - * - * @param *pkt das zu parsene paket - * @return false wenn kein Plugin das Paket angenommen, - * sonst rueckgabewert des Plugins - */ int plugins_parse(struct net_pkt *pkt) { - struct hlswmaster_plugin *plugin; int retval; - pthread_mutex_lock(&plugin_lock); - list_for_each_entry(plugin, &plugin_list, list) { - /* wenn vorhanden die parse funktion aufrufen */ - if (plugin->parse && (retval = plugin->parse(pkt))) { - pthread_mutex_unlock(&plugin_lock); + struct hlswmaster_plugin *plugin; + list_for_each_entry(plugin, &plugin_list, list) + if (plugin->parse && (retval = plugin->parse(pkt))) return retval; - } - } - pthread_mutex_unlock(&plugin_lock); return PARSE_REJECT; } -/** - * plugins_gc() - * ruft die gc() Funktionen der Plugins auf - * - * @param timeout timeout interval in sekunden - * @return true - */ -int plugins_gc(unsigned long timeout) +int plugin_init(void) { - struct hlswmaster_plugin *plugin; + char *dir = config_get_string("global", "plugin_dir", "."); + int cnt = config_get_strings("global", "plugin", plugin_load, (void *)dir); - 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; + log_print(LOG_INFO, "%d plugins loaded", cnt); + return 0; } + diff --git a/plugin.h b/plugin.h index e824535..1dddd7f 100644 --- a/plugin.h +++ b/plugin.h @@ -16,15 +16,16 @@ struct hlswmaster_plugin { struct list_head list; - char name[32]; - void *dlref; - - int (*init)(struct conf_section *config); + + int (*init)(void); int (*fini)(void); int (*scan)(void); int (*parse)(struct net_pkt *pkt); - int (*gc)(int timeout); }; +int plugin_init(); +int plugins_scan(); +int plugins_parse(struct net_pkt *pkt); + #endif /* _PLUGIN_H */ diff --git a/plugin_helper.c b/plugin_helper.c index 368569f..167d840 100644 --- a/plugin_helper.c +++ b/plugin_helper.c @@ -20,55 +20,50 @@ #include #include #include - -#define __USE_GNU #include -#include - -#include -#include -#include -#include - -#include "hlswmaster.h" +#include "gamelist.h" +#include "logging.h" #include "netpkt.h" -#include "plugin.h" #include "plugin_helper.h" +#include "scanner.h" + +int server_add(uint16_t gameid, uint32_t ip, uint16_t port1, uint16_t port2) +{ + struct game_entry *entry = malloc(sizeof(struct game_entry)); + if (entry == NULL) { + log_print(LOG_WARN, "server_add(): out of memory"); + return -1; + } + + entry->gameid = gameid; + entry->ip = ip; + entry->port1 = port1; + entry->port2 = port2; + + return gamelist_add(entry); +} + +int server_add_pkt(unsigned int gameid, struct net_pkt *pkt) +{ + return server_add(gameid, pkt->addr.sin_addr.s_addr, ntohs(pkt->addr.sin_port), 0); +} -/** - * pkt_queue_portarr() - * sendet an eine reihe von ports - * - * @param *dstip destination IP, wenn NULL wird broadcast angenommen - * @param *portarr ports an die gesendet wird - * @param *buf daten die gesendet werden - * @param size groesse der daten - * @return false bei fehler - */ int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *buf, unsigned int size) { unsigned short port; - int ret = 1; + int ret = 0; while (portarr && portarr->portlo) { for (port = portarr->portlo; port <= portarr->porthi; port++) - if (!pkt_send(dstip, port, buf, size)) - ret = 0; + if (pkt_send(dstip, port, buf, size) < 0) + ret = -1; portarr++; } return ret; } -/** - * pkt_check_portarr() - * prueft ob der src-port des pakets in der portliste vorhanden ist - * - * @param *pkt paket vom gameserver - * @param *portarr ports die angenommen werden - * @return die gameid der portrange oder 0 wenn nicht vorhanden - */ int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr) { unsigned short port; @@ -81,225 +76,3 @@ int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr) } return 0; } - -/** - * pkt_memcmp() - * vergleicht das Paket mit einem Speicherbereich - * - * @param *pkt paket vom gameserver - * @param offset offset ab dem verglichen wird - * @param *search daten nach denen gesucht wird - * @param size laenge der daten - * @return false wenn gleich - * - * @todo return false wenn offset + size >= pkt->size ? - */ -int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) -{ - - if (offset >= pkt->size) - return 1; - - /* nicht ueber das paket hinaus vergleichen */ - if (offset + size >= pkt->size) - size = pkt->size - offset; - - return memcmp(pkt->buf + offset, search, size); -} - -/** - * pkt_memmem() - * sucht einen Speicherbereich in dem Paket - * - * @param *pkt paket vom gameserver - * @param offset offset ab dem gesucht wird - * @param *search daten nach denen gesucht wird - * @param size laenge der daten - * @return offset auf den string im Paket, oder -1 wenn nicht gefunden - */ -int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) -{ - void *found; - - if (offset >= pkt->size) - return -1; - - found = memmem(pkt->buf + offset, pkt->size, search, size); - - return (found == NULL) ? -1 : (found - (void *)pkt->buf); -} - -/** - * pkt_merge() - * fuegt zwei pakete zu einem zusammen - * die header werden vom ersten Paket uebernommen - * die pakete werden nicht gefreed(!) - * - * @param *pkt1 erstes Paket - * @param *pkt2 zweites Paket - * @return zusammengefasstes Paket - */ -struct net_pkt * pkt_merge(struct net_pkt *pkt1, struct net_pkt *pkt2) -{ - struct net_pkt *ret; - ret = malloc(sizeof(struct net_pkt) + pkt1->size + pkt2->size); - - memcpy(&ret->addr, &pkt1->addr, sizeof(ret->addr)); - ret->size = pkt1->size + pkt2->size; - - memcpy(ret->buf, pkt1->buf, pkt1->size); - memcpy(ret->buf + pkt1->size, pkt2->buf, pkt2->size); - - return ret; -} - -/** - * server_add_pkt() - * fuegt der serverliste einen server hinzu - * - * @param gameid gameid des gameservers - * @param *pkt daten vom gameserver (fuer ip/port) - * @return false bei fehler - */ -int server_add_pkt(unsigned int gameid, struct net_pkt *pkt) -{ - return server_add(gameid, pkt->addr.sin_addr.s_addr, ntohs(pkt->addr.sin_port), 0); -} - -/** - * pkt_ntoa() - * gibt die IP des Pakets als String zurueck - * - * @param *pkt daten vom gameserver - * @return pointer auf String - */ -char * pkt_ntoa(struct net_pkt *pkt) -{ - return inet_ntoa(pkt->addr.sin_addr); -} - -/** - * pkt_getport() - * gibt den Port des Pakets als short zurueck - * - * @param *pkt daten vom gameserver - * @return portnummer - */ -unsigned short pkt_getport(struct net_pkt *pkt) -{ - return ntohs(pkt->addr.sin_port); -} - -/** - * pkt_sameaddr() - * vergleicht die Adressen zweier Pakete - * - * @param *pkt1 Paket 1 - * @param *pkt2 Paket 2 - * @return true wenn Adressen gleich - */ -int pkt_sameaddr(struct net_pkt *pkt1, struct net_pkt *pkt2) -{ - return (pkt1->addr.sin_addr.s_addr == pkt2->addr.sin_addr.s_addr) && - (pkt1->addr.sin_port == pkt2->addr.sin_port); -} - -/** - * pkt_parse_int() - * gibt die dezimalzahl in dem paket ab einer position zurueck - * - * @param *pkt daten vom gameserver - * @param offset offset auf den begin des integers - * @param *val pointer auf das ergebnis - * @return Anzahl der gelesenen Zeichen oder 0 bei fehler - */ -int pkt_parse_int(struct net_pkt *pkt, unsigned int offset, int *val) -{ - unsigned char *max = pkt->buf + pkt->size; - unsigned char *c = pkt->buf + offset; - - /* untere grenze abtesten */ - if (pkt->buf > c || c > max) - return -1; - - *val = 0; - - /* ziffern einlesen */ - while (isdigit(*c) && c < max) - *val = (*val * 10) + (*c++ - 0x30); - - return (c - (pkt->buf + offset)); -} - -/** - * pkt_parse_ip() - * gibt die IP in *.*.*.* ab p als integer zurueck - * - * @param *pkt daten vom gameserver - * @param offset offset auf den begin der IP - * @param *ip pointer auf eine in_addr struct - * @return Anzahl der gelesenen Zeichen oder 0 bei Fehler - */ -int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip) -{ - int i, tmp, count, pos = offset; - ip->s_addr = 0; - - for (i = 0; i < 4; i++) { - count = pkt_parse_int(pkt, pos, &tmp); - pos += count; - if (count == 0 || tmp < 0 || tmp > 255) - return 0; - - ip->s_addr = ip->s_addr>>8 | tmp<<24; - - if (i != 3 && pkt->buf[pos++] != '.') - return 0; - } - return pos - offset; -} - -/** - * pkt_print() - * gibt ein paket als hex-dump aus - * - * @param *pkt daten vom gameserver - * @return char pointer auf hex-dump - */ -char * pkt_print(struct net_pkt *pkt) -{ - int pos = 0, i = 0, j; - char *buf = malloc(pkt->size * 4 + 64); - - while (pos < pkt->size) { - i += sprintf(buf + i, "%04X: ", pos); - for (j = 0; j < 16; j++) { - if (pos + j < pkt->size) - i += sprintf(buf + i, "%02X", pkt->buf[pos + j]); - else - i += sprintf(buf + i, " "); - - if (j % 2) - buf[i++] = ' '; - } - - for (j = 0; j < 16; j++) { - if (pos + j < pkt->size) { - unsigned char val = pkt->buf[pos + j]; - if (val >= 0x20 && val < 0x80) - buf[i++] = val; - else - buf[i++] = '.'; - } else { - buf[i++] = ' '; - } - } - - pos += 16; - buf[i++] = '\r'; - buf[i++] = '\n'; - } - buf[i] = 0; - return buf; -} - diff --git a/plugin_helper.h b/plugin_helper.h index c293c28..320cfa8 100644 --- a/plugin_helper.h +++ b/plugin_helper.h @@ -1,35 +1,15 @@ #ifndef _PLUGIN_HELPER_H #define _PLUGIN_HELPER_H -#include - -#include "netpkt.h" -#include "list.h" -#include "plugin.h" - -extern int server_add(uint16_t gameid, uint32_t ip, uint16_t port1, uint16_t port2); - struct scan_ports { unsigned short portlo; unsigned short porthi; unsigned short gameid; }; -extern int pkt_send(struct in_addr *dstip, unsigned int dstport, char *buf, unsigned int size); -extern int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *buf, unsigned int size); - -extern int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr); -extern int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); -extern int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); -extern struct net_pkt * pkt_merge(struct net_pkt *pkt1, struct net_pkt *pkt2); - -extern int server_add_pkt(unsigned int gameid, struct net_pkt *pkt); - -extern char * pkt_ntoa(struct net_pkt *pkt); -extern unsigned short pkt_getport(struct net_pkt *pkt); -extern int pkt_sameaddr(struct net_pkt *pkt1, struct net_pkt *pkt2); -extern int pkt_parse_int(struct net_pkt *pkt, unsigned int offset, int *val); -extern int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip); -extern char * pkt_print(struct net_pkt *pkt); +int server_add(uint16_t gameid, uint32_t ip, uint16_t port1, uint16_t port2); +int server_add_pkt(unsigned int gameid, struct net_pkt *pkt); +int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *buf, unsigned int size); +int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr); #endif /* _PLUGIN_HELPER_H */ diff --git a/scanner.c b/scanner.c index 5e50e33..438ccfb 100644 --- a/scanner.c +++ b/scanner.c @@ -21,381 +21,153 @@ #include #include #include + #include #include #include #include #include -#include -#include -#include -#include -#include "list.h" +#include "event.h" #include "netpkt.h" #include "configfile.h" -#include "hlswmaster.h" #include "plugin.h" -#include "plugin_helper.h" +#include "scanner.h" +#include "logging.h" +#include "gamelist.h" -#define DEBUG 1 +static LIST_HEAD(tx_queue); -struct rx_entry { - struct list_head list; - struct net_pkt *pkt; -}; - -/** rx packet liste */ -static LIST_HEAD(rxlist); - -/** sichert die rx liste */ -static pthread_mutex_t rxlist_lock = PTHREAD_MUTEX_INITIALIZER; - -/** anzahl der pakete in der rxlist */ -static sem_t rxlist_sem; - -/** interne serverliste */ -static LIST_HEAD(serverlist); - -/** sichert die interne serverliste */ -static pthread_mutex_t serverlist_lock = PTHREAD_MUTEX_INITIALIZER; - -/** scan socket */ static int scan_sock; -/** - * server_cmp() - * LIST_FIND helper - * - * @param *a gameserver 1 - * @param *b gameserver 2 - * @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() - * wenn der server noch nicht in der internen liste vorhanden ist - * wird er hinzugefuegt, ansonsten nur die modtime angepasst - * - * @param gameid gameid des servers - * @param ip ip des servers - * @param port1 erster port - * @param port2 zweiter port - * @return false bei fehler - */ -int server_add(uint16_t gameid, uint32_t ip, uint16_t port1, uint16_t port2) -{ - struct game_server server, *nserver; - - server.gameid = gameid; - server.ip = ip; - server.port1 = port1; - server.port2 = port2; - - pthread_mutex_lock(&serverlist_lock); - - /* diesen server in der liste suchen */ - nserver = LIST_FIND(&serverlist, server_cmp, struct game_server *, &server); - if (!nserver) { - /* neuen eintrag anlegen */ - if (!(nserver = malloc(sizeof(struct game_server)))) { - pthread_mutex_unlock(&serverlist_lock); - log_print("server_add(): malloc()"); - return 0; - } -#ifdef DEBUG - { - struct in_addr tmp; - tmp.s_addr = server.ip; - printf("server_add_new: gameid=%2d ip=%15s port1=%5d port2=%5d\n", - server.gameid, inet_ntoa(tmp), server.port1, server.port2); - } -#endif - memcpy(nserver, &server, sizeof(struct game_server)); - list_add_tail(&nserver->list, &serverlist); - } - - /* modtime anpassen */ - nserver->modtime = time(NULL); - - pthread_mutex_unlock(&serverlist_lock); - return 1; -} - -/** - * serverlist_refresh() - * loescht alte server aus der liste - * baut aus den verbleibenden die client_liste auf - * - * @param timeout timeout in sekunden - */ -static void serverlist_refresh(long timeout) -{ - struct game_server *server, *tmp; - long now = time(NULL); - - pthread_mutex_lock(&serverlist_lock); - list_for_each_entry_safe(server, tmp, &serverlist, list) { - if ((server->modtime + timeout) < now) { -#ifdef DEBUG - { - struct in_addr tmp2; - tmp2.s_addr = server->ip; - printf("server timeout: gameid=%2d ip=%15s port1=%5d port2=%5d\n", - server->gameid, inet_ntoa(tmp2), server->port1, server->port2); - } -#endif - list_del(&server->list); - free(server); - - } else { - client_pkt_add(server); - } - } - pthread_mutex_unlock(&serverlist_lock); - - client_pkt_commit(); -} - -/** - * pkt_send() - * schickt ein paket ab - * - * @param *dstip destination IP, wenn NULL wird broadcast angenommen - * @param dstport destination port - * @param *buf datenbereich der gesendet wird - * @param size groesse der daten - * @return false bei fehler - */ int pkt_send(struct in_addr *dstip, unsigned int dstport, char *buf, unsigned int size) { - struct sockaddr_in addr; - int ret = 1; - - addr.sin_family = AF_INET; - addr.sin_port = htons(dstport); - addr.sin_addr.s_addr = (dstip ? dstip->s_addr : 0xFFFFFFFF); - - if (sendto(scan_sock, buf, size, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - log_print("scan_send(): sendto()"); - ret = 0; + struct net_pkt *pkt = malloc(sizeof(struct net_pkt) + size); + if (pkt == NULL) { + log_print(LOG_WARN, "pkt_send(): out of memory"); + return -1; } - usleep(10000); - return ret; + pkt->addr.sin_family = AF_INET; + pkt->addr.sin_port = htons(dstport); + pkt->addr.sin_addr.s_addr = (dstip ? dstip->s_addr : 0xFFFFFFFF); + + memcpy(pkt->buf, buf, size); + pkt->size = size; + + list_add_tail(&pkt->list, &tx_queue); + return 0; } -/** - * scan_receive_real() - * arbeitet die receive queue ab - */ -void scan_receive_real(void) +static int scanner_transmit(void *privdata) { - struct rx_entry *rx; + if (list_empty(&tx_queue)) + return -1; + struct net_pkt *pkt; - int cnt, retval; + pkt = list_entry(tx_queue.next, struct net_pkt, list); + list_del(&pkt->list); - do { - sem_wait(&rxlist_sem); - - pthread_mutex_lock(&rxlist_lock); - rx = list_entry(rxlist.next, struct rx_entry, list); - list_del(&rx->list); - pthread_mutex_unlock(&rxlist_lock); + int ret = sendto(scan_sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&pkt->addr, sizeof(pkt->addr)); + if (ret <= 0) + log_print(LOG_WARN, "scanner_transmit(): sendto()"); - pkt = rx->pkt; - free(rx); - - retval = plugins_parse(pkt); - - switch (retval) { - case PARSE_REJECT: - log_print("scan_receive(): unknown packet: %s:%d size:%d", - inet_ntoa(pkt->addr.sin_addr), - ntohs(pkt->addr.sin_port), - pkt->size); -#if 0 - { - char *p = pkt_print(pkt); - log_print("%s", p); - free(p); - } -#endif - - case PARSE_ACCEPT: - free(pkt); - - case PARSE_ACCEPT_FREED: - break; - } - - sem_getvalue(&rxlist_sem, &cnt); - } while (cnt > 0); - + return 0; } -/** - * scan_control() - * triggert den gc der plugins - * triggert den serverlist_refresh - * triggert den scan der plugins - * arbeitet die rx queue ab - */ -void scan_control(void) +static int scanner_scan(void *privdata) { - long last_plugin_gc = 0, last_list_refresh = 0, last_scan = 0; - int plugin_timeout, list_timeout, list_refresh, scan_interval; - int cnt; - long now; - - plugin_timeout = config_get_int("global", "plugin_timeout", 30); - list_timeout = config_get_int("global", "serverlist_timeout", 30); - list_refresh = config_get_int("global", "serverlist_refresh", 5); - scan_interval = config_get_int("global", "scan_interval", 30); + plugins_scan(); - log_print("thread_start: scan_control"); - log_print(" scan_interval: %ds", scan_interval); - log_print(" serverlist_refresh: %ds", list_refresh); - log_print(" serverlist_timeout: %ds", list_timeout); - log_print(" plugin_timeout: %d", plugin_timeout); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10000; - while (1) { - now = time(NULL); - - /* interne plugin daten aufraeumen */ - if (last_plugin_gc + plugin_timeout < now) { - last_plugin_gc = now; - plugins_gc(plugin_timeout); - } - - /* server liste aufraeumen, und neue client pkts erzeugen */ - if (last_list_refresh + list_refresh < now) { - last_list_refresh = now; - serverlist_refresh(list_timeout); - } - - /* neuen scan ausloesen */ - if (last_scan + scan_interval < now) { - last_scan = now; - plugins_scan(); - } - - /* empfangene daten parsen */ - sem_getvalue(&rxlist_sem, &cnt); - if (cnt > 0) - scan_receive_real(); - - usleep(500000); - } + event_add_timeout(&tv, scanner_transmit, NULL); + return 0; } -/** - * scan_receive() - * wartet auf serverantworten und uebergibt die pakete der - * receive queue - */ -void scan_receive(void) +static int scanner_receive(int fd, void *privdata) { - struct net_pkt *pkt; - struct rx_entry *rx; - fd_set fdsel, fdcpy; - int recvsize, i; - - log_print("thread_start: scan_receiver"); - - FD_ZERO(&fdsel); - FD_SET(scan_sock, &fdsel); - - while (1) { - memcpy(&fdcpy, &fdsel, sizeof(fdsel)); - select(FD_SETSIZE, &fdcpy, NULL, NULL, NULL); - - if (ioctl(scan_sock, FIONREAD, &recvsize) == -1) { - log_print("scan_receive(): ioctl()"); - continue; - } - - if (!(pkt = malloc(sizeof(struct net_pkt) + recvsize))) { - log_print("scan_receive(): malloc()"); - continue; - } - - i = sizeof(struct sockaddr_in); - pkt->size = recvfrom(scan_sock, pkt->buf, recvsize, 0, (struct sockaddr *)&pkt->addr, &i); - - if (pkt->size == 0) { - free(pkt); - continue; - } - - rx = malloc(sizeof(struct rx_entry)); - rx->pkt = pkt; - - pthread_mutex_lock(&rxlist_lock); - list_add_tail(&rx->list, &rxlist); - pthread_mutex_unlock(&rxlist_lock); - - sem_post(&rxlist_sem); - } -} - -/** - * schliesst den server scan socket - */ -static void scan_close() -{ - close(scan_sock); - sem_destroy(&rxlist_sem); -} - -/** - * scan_init() - * initialisiert den socket fuer den server scan - * initialisiert die rxlist semaphore - * - * @return false on error - */ -int scan_init() -{ - struct sockaddr_in dst; - int i = 1, port; - char *ip; - - if ((scan_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - log_print("scan_init(): socket()"); + int recvsize; + if (ioctl(fd, FIONREAD, &recvsize) == -1) { + log_print(LOG_WARN, "scanner_receive(): ioctl(FIONREAD)"); return 0; } - ip = config_get_string("global", "scan_ip", "0.0.0.0"); - port = config_get_int("global", "scan_port", 7130); - - dst.sin_family = AF_INET; - dst.sin_port = htons(port); - inet_aton(ip, &dst.sin_addr); - - if (bind(scan_sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { - log_print("scan_init(): bind()"); + struct net_pkt *pkt = malloc(sizeof(struct net_pkt) + recvsize); + if (pkt == NULL) { + log_print(LOG_WARN, "scanner_receive(): out of memory"); return 0; } - if (setsockopt(scan_sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))) { - log_print("scan_init(): setsockopt()"); + int i = sizeof(struct sockaddr_in); + pkt->size = recvfrom(fd, pkt->buf, recvsize, 0, (struct sockaddr *)&pkt->addr, &i); + + if (pkt->size < 0) { + log_print(LOG_WARN, "scanner_receive(): recvfrom()"); + free(pkt); return 0; } - if (atexit(scan_close) != 0) { - log_print("scan_init(): atexit()"); - return 0; + switch (plugins_parse(pkt)) { + case PARSE_REJECT: + log_print(LOG_INFO, "scanner_receive(): unknown packet: %s:%d size:%d", + inet_ntoa(pkt->addr.sin_addr), + ntohs(pkt->addr.sin_port), + pkt->size); + + case PARSE_ACCEPT: + free(pkt); + + case PARSE_ACCEPT_FREED: + break; } - log_print("scan socket initialized (%s:%d)", ip, port); - - sem_init(&rxlist_sem, 0, 0); - - return 1; + return 0; +} + +int scanner_init() +{ + scan_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (scan_sock < 0) { + log_print(LOG_ERROR, "scan_init(): socket()"); + return -1; + } + + struct sockaddr_in src; + char *addr = config_get_string("global", "scanner_src", "0.0.0.0:7130"); + if (parse_saddr(addr, &src) != 0) { + log_print(LOG_ERROR, "server_init(): invalid scanner_src '%s'", addr); + return -1; + } + + if (bind(scan_sock, (struct sockaddr *)&src, sizeof(src)) < 0) { + log_print(LOG_ERROR, "scan_init(): bind()"); + return -1; + } + + int j = 1; + if (setsockopt(scan_sock, SOL_SOCKET, SO_BROADCAST, &j, sizeof(j))) { + log_print(LOG_ERROR, "scan_init(): setsockopt(SO_BROADCAST)"); + return -1; + } + + int rxsize = 1<<20; + if (setsockopt(scan_sock, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize))) { + log_print(LOG_ERROR, "scan_init(): setsockopt(SO_RCVBUF)"); + return -1; + } + + event_add_readfd(scan_sock, scanner_receive, NULL); + log_print(LOG_INFO, "scan socket initialized (%s:%d)", inet_ntoa(src.sin_addr), ntohs(src.sin_port)); + + struct timeval tv; + tv.tv_sec = config_get_int("global", "scan_interval", 60); + tv.tv_usec = 0; + + event_add_timeout(&tv, scanner_scan, NULL); + + scanner_scan(NULL); + return 0; } diff --git a/scanner.h b/scanner.h new file mode 100644 index 0000000..7da87c2 --- /dev/null +++ b/scanner.h @@ -0,0 +1,7 @@ +#ifndef _SCANNER_H_ +#define _SCANNER_H_ + +int pkt_send(struct in_addr *dstip, unsigned int dstport, char *buf, unsigned int size); +int scanner_init(); + +#endif /* _SCANNER_H_ */ diff --git a/server.c b/server.c new file mode 100644 index 0000000..f72d232 --- /dev/null +++ b/server.c @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 11/2006 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 "configfile.h" +#include "list.h" +#include "event.h" +#include "scanner.h" +#include "logging.h" +#include "netpkt.h" +#include "gamelist.h" + +#define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00" +#define HLSW_HEADER_LEN 0x12 +#define HLSW_ENTRY_LEN 10 +#define MAX_PKT_LEN (HLSW_HEADER_LEN + HLSW_ENTRY_LEN * 140) + +static LIST_HEAD(master_pkt_list); + +static int serverlist_add_game(struct game_entry *entry) +{ + int found = 0; + + struct net_pkt *pkt; + list_for_each_entry(pkt, &master_pkt_list, list) { + if (pkt->size <= (MAX_PKT_LEN - HLSW_ENTRY_LEN)) { + found = 1; + break; + } + } + + if (!found) { + pkt = malloc(sizeof(struct net_pkt) + MAX_PKT_LEN); + if (pkt == NULL) { + log_print(LOG_WARN, "serverlist_add_game(): out of memory"); + return -1; + } + + memcpy(pkt->buf, HLSW_HEADER, HLSW_HEADER_LEN); + pkt->size = HLSW_HEADER_LEN; + list_add(&pkt->list, &master_pkt_list); + } + + memcpy((void *)&pkt->buf + pkt->size, &entry->gameid, HLSW_ENTRY_LEN); + pkt->size += HLSW_ENTRY_LEN; + return 0; +} + +static int serverlist_refresh(void *privdata) +{ + int timeout = (int)privdata; + + struct net_pkt *pkt, *tmp; + list_for_each_entry_safe(pkt, tmp, &master_pkt_list, list) { + list_del(&pkt->list); + free(pkt); + } + + gamelist_gc_and_dump(serverlist_add_game, timeout); + return 0; +} + +static int server_handler(int fd, void *privdata) +{ + struct sockaddr_in client; + unsigned char buf[32]; + + int i = sizeof(client); + int ret = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client, &i); + if (ret <= 0) { + log_print(LOG_WARN, "server_handler(): recvfrom()"); + return 0; + } + + if (memcmp(buf, HLSW_HEADER, HLSW_HEADER_LEN)) + return 0; + + struct net_pkt *pkt; + list_for_each_entry(pkt, &master_pkt_list, list) + sendto(fd, pkt->buf, pkt->size, 0, (struct sockaddr *)&client, sizeof(client)); + + return 0; +} + +int server_init() +{ + int sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + log_print(LOG_ERROR, "server_init(): socket()"); + return -1; + } + + struct sockaddr_in src; + char *addr = config_get_string("global", "master_src", "0.0.0.0:7140"); + if (parse_saddr(addr, &src) != 0) { + log_print(LOG_ERROR, "server_init(): invalid master_src '%s'", addr); + return -1; + } + + if (bind(sock, (struct sockaddr *)&src, sizeof(src)) < 0) { + log_print(LOG_ERROR, "server_init(): bind()"); + return -1; + } + + event_add_readfd(sock, server_handler, NULL); + + struct timeval tv; + tv.tv_sec = config_get_int("global", "serverlist_refresh", 5); + tv.tv_usec = 0; + + int timeout = config_get_int("global", "serverlist_timeout", 180); + event_add_timeout(&tv, serverlist_refresh, (void *)timeout); + return 0; +} diff --git a/server.h b/server.h new file mode 100644 index 0000000..256ef39 --- /dev/null +++ b/server.h @@ -0,0 +1,6 @@ +#ifndef _SERVER_H_ +#define _SERVER_H_ + +int server_init(); + +#endif /* _SERVER_H_ */