From ce59a8853e6731b6580b44a4f693b137ecf0a505 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Fri, 24 Nov 2006 21:53:25 +0100 Subject: [PATCH] rewrite --- Makefile | 35 ++++ client.c | 196 ------------------ config.c | 263 ------------------------ configfile.c | 230 +++++++++++++++++++++ configfile.h | 18 ++ event.c | 310 +++++++++++++++++++++++++++++ event.h | 26 +++ gamelist.c | 85 ++++++++ gamelist.h | 25 +++ main.c => hlswmaster.c | 46 +++-- hlswmaster.conf | 56 +++--- hlswmaster.h | 42 ---- list.h | 6 +- logging.c | 110 +++++----- logging.h | 16 ++ masterquery.c | 26 +-- netpkt.c | 143 +++++++++++++ netpkt.h | 14 ++ p_d3engine.c | 4 +- p_gamespy1.c | 56 ++++-- p_gamespy2.c | 14 +- p_halflife.c | 145 +++++++------- p_hlswproxy.c | 92 +++++---- p_q3engine.c | 18 +- p_quake2.c | 5 +- p_ut2k4.c | 3 + plugin.c | 193 ++++++------------ plugin.h | 11 +- plugin_helper.c | 281 +++----------------------- plugin_helper.h | 28 +-- scanner.c | 442 ++++++++++------------------------------- scanner.h | 7 + server.c | 138 +++++++++++++ server.h | 6 + 34 files changed, 1563 insertions(+), 1527 deletions(-) create mode 100644 Makefile delete mode 100644 client.c delete mode 100644 config.c create mode 100644 configfile.c create mode 100644 configfile.h create mode 100644 event.c create mode 100644 event.h create mode 100644 gamelist.c create mode 100644 gamelist.h rename main.c => hlswmaster.c (79%) delete mode 100644 hlswmaster.h create mode 100644 logging.h create mode 100644 netpkt.c create mode 100644 scanner.h create mode 100644 server.c create mode 100644 server.h 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_ */