This commit is contained in:
Olaf Rempel 2006-11-24 21:53:25 +01:00
parent 2363f1de47
commit ce59a8853e
34 changed files with 1563 additions and 1527 deletions

35
Makefile Normal file
View File

@ -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)

196
client.c
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <pthread.h>
#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);
}
}

263
config.c
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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(&section->list);
INIT_LIST_HEAD(&section->tupel);
strncpy(section->name, name, sizeof(section->name));
list_add_tail(&section->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, &section->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, &section->tupel, list) {
list_del(&tupel->list);
free(tupel->option);
free(tupel->parameter);
free(tupel);
}
list_del(&section->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, &section->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, &section->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;
}

230
configfile.c Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#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(&section->list);
INIT_LIST_HEAD(&section->tupel_list);
section->name = strdup(name);
if (section->name == NULL) {
free(section);
return NULL;
}
list_add_tail(&section->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, &section->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, &section->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, &section->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;
}

18
configfile.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <netinet/in.h>
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_ */

310
event.c Normal file
View File

@ -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 <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#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);
}
}
}
}
}

26
event.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _EVENT_H_
#define _EVENT_H_
#include <sys/time.h>
#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_ */

85
gamelist.c Normal file
View File

@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}

25
gamelist.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _GAMELIST_H_
#define _GAMELIST_H_
#include <inttypes.h>
#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_ */

View File

@ -21,13 +21,18 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <pthread.h>
#include <getopt.h> #include <getopt.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <pwd.h> #include <pwd.h>
#include "hlswmaster.h"
#include "configfile.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[] = { static struct option opts[] = {
{"config", 1, 0, 'c'}, {"config", 1, 0, 'c'},
@ -39,8 +44,6 @@ static struct option opts[] = {
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
pthread_t thread1, thread2, thread3;
pthread_attr_t attr;
int arg = 0, code = 0, debug = 0; int arg = 0, code = 0, debug = 0;
char *config = NULL, *user = NULL, *logfile; char *config = NULL, *user = NULL, *logfile;
@ -84,22 +87,22 @@ int main(int argc, char *argv[])
if (user) { if (user) {
struct passwd *pwl; struct passwd *pwl;
if (!(pwl = getpwnam(user))) { if (!(pwl = getpwnam(user))) {
log_print("unknown user: %s", user); log_print(LOG_ERROR, "unknown user: %s", user);
exit(-1); exit(-1);
} }
if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) { if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) {
log_print("setgid/setuid"); log_print(LOG_ERROR, "setgid/setuid");
exit(-1); exit(-1);
} }
} }
/* parse config file */ /* parse config file */
if (!config_parse(config)) if (config_parse(config) == -1)
exit(-1); exit(-1);
/* check logfile */ /* check logfile */
logfile = config_get_string("global", "logfile", NULL); logfile = config_get_string("global", "logfile", "hlswmaster.log");
if (logfile && !debug) { if (logfile && !debug) {
/* start logging */ /* start logging */
if (!log_init(logfile)) if (!log_init(logfile))
@ -109,26 +112,25 @@ int main(int argc, char *argv[])
daemon(-1, 0); 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 */ /* init plugins */
if (!scan_init()) if (plugin_init() == -1)
exit(-1); exit(-1);
/* load plugins */ /* init gamelist */
plugin_load_all(); if (gamelist_init() == -1)
exit(-1);
/* startup threads */ /* init server */
pthread_attr_init(&attr); if (server_init() == -1)
pthread_attr_setstacksize(&attr, 65536); exit(-1);
pthread_create(&thread1, &attr, (void *)&scan_control, NULL); /* init scanner */
pthread_create(&thread2, &attr, (void *)&scan_receive, NULL); if (scanner_init() == -1)
pthread_create(&thread3, &attr, (void *)&client_handler, NULL); exit(-1);
/* wait untill d00msday */ event_loop();
while (1)
sleep(3600);
return 0; return 0;
} }

View File

@ -1,43 +1,43 @@
[global] [global]
# broadcast scan source IP & PORT (udp) ## broadcast scan source IP & PORT (udp)
scan_ip 0.0.0.0 #scanner_src 0.0.0.0:7130
scan_port 7130
# broadcast scan every X seconds ## broadcast scan every X seconds
scan_interval 60 #scan_interval 60
# serverlist rebuild every X seconds ## serverlist rebuild every X seconds
serverlist_interval 5 #serverlist_refresh 5
# server timeout after X seconds ## server timeout after X seconds
serverlist_timeout 180 #serverlist_timeout 180
# plugin data timeout every X seconds ## master answers with this source IP
plugin_timeout 90 #master_src 0.0.0.0:7140
# master answers with this source IP #plugin_dir .
master_ip 0.0.0.0 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 ## logging
#plugin plugins/.libs/hlswproxy.so #logfile hlswmaster.log
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 [gamespy1]
logfile hlswmaster.log ## internal fragment-list timeout
#gc_timeout 90
[hlswproxy] [hlswproxy]
# ask these hlswmasters ## ask these hlswmasters
scan_ip 10.10.0.1 scan 10.10.0.1:7140
scan_ip 10.10.0.2 scan 10.10.0.2:7140
[halflife] [halflife]
# allow these nets ## allow these nets
valid_net 10.10.0.0/16 valid_net 10.10.0.0/16
valid_net 172.16.0.0/16 valid_net 172.16.0.0/16

View File

@ -1,42 +0,0 @@
#ifndef _HLSWMASTER_H
#define _HLSWMASTER_H
#include <inttypes.h>
#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 */

6
list.h
View File

@ -1,5 +1,5 @@
#ifndef _LIST_H #ifndef _LIST_H_
#define _LIST_H #define _LIST_H_
/* /*
* stolen from linux kernel 2.6.11 (http://kernel.org/) * 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; \ (type)__j; \
}) })
#endif /* _LIST_H */ #endif /* _LIST_H_ */

110
logging.c
View File

@ -1,5 +1,5 @@
/*************************************************************************** /***************************************************************************
* Copyright (C) 03/2005 by Olaf Rempel * * Copyright (C) 06/2006 by Olaf Rempel *
* razzor@kopf-tisch.de * * razzor@kopf-tisch.de *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
@ -24,84 +24,80 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include "configfile.h" #include "logging.h"
#define BUFSIZE 8192
static FILE *log_fd = NULL; static FILE *log_fd = NULL;
static char *buffer = NULL;
/** void log_print(int prio, const char *fmt, ...)
* 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, ...)
{ {
va_list az; va_list az;
char *buffer = malloc(8192); int len;
va_start(az, fmt); if (buffer == NULL) {
vsprintf(buffer, fmt, az); buffer = malloc(BUFSIZE);
va_end(az); if (buffer == NULL) {
fprintf(stderr, "log_print: out of memory\nBailing out!\n");
/* ins logfile loggen */ exit(-1);
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);
} }
} }
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; errno = 0;
free(buffer);
} }
/** static void log_close(void)
* log_close()
* schliesst das logfile
*/
static void log_close()
{ {
if (buffer)
free(buffer);
fclose(log_fd); fclose(log_fd);
} }
/**
* log_init()
* initialisiert das logging
*
* @param logfile filename des logfiles
* @return false bei fehler
*/
int log_init(char *logfile) int log_init(char *logfile)
{ {
if ((log_fd = fopen(logfile, "a" )) == NULL) { log_fd = fopen(logfile, "a");
log_print("log_open('%s'): %s", logfile); if (log_fd == NULL) {
log_print(LOG_ERROR, "log_open('%s'): %s", logfile);
return 0; return 0;
} }
/* beim beenden log schliessen */
if (atexit(log_close) != 0) { if (atexit(log_close) != 0) {
log_print("log_open(): atexit()"); log_print(LOG_ERROR, "log_open(): atexit()");
return 0; return 0;
} }
log_print("=========================="); log_print(LOG_EVERYTIME, "==========================");
return 1; return 1;
} }

16
logging.h Normal file
View File

@ -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_ */

View File

@ -22,7 +22,7 @@ struct _entry {
static char hlswheader[] = "\xFF\xFF\xFF\xFFHLSWLANSEARCH"; static char hlswheader[] = "\xFF\xFF\xFF\xFFHLSWLANSEARCH";
static char *id2name[] = { static char *id2name[] = {
"Unknown", // 0 "Unknown", // 0
"Halflife", "Halflife",
"Quake 1", "Quake 1",
"Quake 2", "Quake 2",
@ -53,7 +53,7 @@ static char *id2name[] = {
"Medal of Honor: Allied Assault Breakthrough", "Medal of Honor: Allied Assault Breakthrough",
"Tribes 2", "Tribes 2",
"Halo", // 30 "Halo", // 30
"Call of Duty", "Call of Duty",
"Savage: The Battle for Newerth", "Savage: The Battle for Newerth",
"Unreal Tournament 2004", "Unreal Tournament 2004",
"HLSteam", "HLSteam",
@ -68,8 +68,8 @@ static char *id2name[] = {
"Starwars: Battlefront (?)", "Starwars: Battlefront (?)",
"SWAT 4", "SWAT 4",
"Battlefield 2", // 45 "Battlefield 2", // 45
"???", "(unknown)",
"Quake 4 (???)", "Quake 4 (?)",
"Call of Duty 2" "Call of Duty 2"
}; };
@ -86,9 +86,9 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size)
} else { } else {
printf("received hlsw packet from: %15s:%-5d size=%d count=%d\n", 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)); ((size > sizeof(hlswheader)) ? (size - sizeof(hlswheader)) / sizeof(struct _entry) : 0));
if (verbose) { if (verbose) {
server = pkt + sizeof(hlswheader); server = pkt + sizeof(hlswheader);
while ((void *)server < pkt + size) { 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() static int scan_init()
{ {
int i = 1; int i = 1;
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket()"); perror("socket()");
return -1; return -1;
@ -115,7 +115,7 @@ static int scan_init()
perror("setsockopt()"); perror("setsockopt()");
return -1; return -1;
} }
return 0; return 0;
} }
@ -139,10 +139,10 @@ static int scan_receive()
FD_ZERO(&fdsel); FD_ZERO(&fdsel);
FD_SET(sock, &fdsel); FD_SET(sock, &fdsel);
tv.tv_sec = 1; tv.tv_sec = 1;
tv.tv_usec = 0; tv.tv_usec = 0;
/* timeout */ /* timeout */
while (tv.tv_sec > 0 || tv.tv_usec > 0) { while (tv.tv_sec > 0 || tv.tv_usec > 0) {
@ -156,7 +156,7 @@ static int scan_receive()
perror("ioctl()"); perror("ioctl()");
return -1; return -1;
} }
if (recvsize > 0) { if (recvsize > 0) {
if (!(pkt = malloc(recvsize))) { if (!(pkt = malloc(recvsize))) {
perror("malloc()"); perror("malloc()");
@ -202,7 +202,7 @@ int main(int argc, char *argv[])
exit(-1); exit(-1);
} }
break; break;
case 'i': /* intervall */ case 'i': /* intervall */
freq = atoi(optarg); freq = atoi(optarg);
if (freq < 1) { if (freq < 1) {
@ -214,7 +214,7 @@ int main(int argc, char *argv[])
case 'v': /* verbose */ case 'v': /* verbose */
verbose = 1; verbose = 1;
break; break;
case 'h': /* help */ case 'h': /* help */
printf("Usage: masterquery [options]\n" printf("Usage: masterquery [options]\n"
"Options: \n" "Options: \n"

143
netpkt.c Normal file
View File

@ -0,0 +1,143 @@
#include <stdio.h>
#define __USE_GNU
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#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;
}

View File

@ -5,10 +5,24 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include "list.h"
struct net_pkt { struct net_pkt {
struct list_head list;
struct sockaddr_in addr; struct sockaddr_in addr;
unsigned int size; unsigned int size;
unsigned char buf[0]; 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 */ #endif /* _NETPKT_H */

View File

@ -18,6 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <string.h> #include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
static struct scan_ports port_arr[] = { 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))) if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT; return PARSE_REJECT;
if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg)))
return PARSE_REJECT; return PARSE_REJECT;

View File

@ -17,7 +17,14 @@
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "logging.h"
#include "event.h"
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
static struct scan_ports port_arr[] = { 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 net_pkt *retpkt = NULL;
struct gs1_part *part, *tmp; struct gs1_part *part, *tmp;
unsigned int i = 1, found = 0, notfound = 0; unsigned int i = 1, found = 0, notfound = 0;
// wenn paket non-final ist, in liste speichern // wenn paket non-final ist, in liste speichern
// NULL zurueckgeben // NULL zurueckgeben
if (pkt_memmem(pkt, 0, search_final, strlen(search_final)) == -1) { 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 */ /* merge error, eat last paket, cleanup */
if (i != subid || notfound > subid) { if (i != subid || notfound > subid) {
log_print("gs1: merging error!"); log_print(LOG_INFO, "gs1: merging error!");
free(pkt); free(pkt);
return NULL; 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) static int parse_real(struct net_pkt *pkt, int gameid)
{ {
int port, offset, pos1, pos2; int port, offset, pos1, pos2;
pos1 = pkt_memmem(pkt, 0, search_gamename, strlen(search_gamename)); pos1 = pkt_memmem(pkt, 0, search_gamename, strlen(search_gamename));
pos1 += strlen(search_gamename); pos1 += strlen(search_gamename);
pos2 = pkt_memmem(pkt, 0, search_gameid, strlen(search_gameid)); pos2 = pkt_memmem(pkt, 0, search_gameid, strlen(search_gameid));
pos2 += strlen(search_gameid); pos2 += strlen(search_gameid);
switch (gameid) { switch (gameid) {
case 5:/* unreal tournament */ case 5:/* unreal tournament */
if (!pkt_memcmp(pkt, pos1, reply_ut, strlen(reply_ut))) 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 */ /* unreal tournament 2k4 */
else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4))) else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4)))
gameid = 33; gameid = 33;
/* americas army operations */ /* americas army operations */
else if (!pkt_memcmp(pkt, pos1, reply_aao, strlen(reply_aao))) else if (!pkt_memcmp(pkt, pos1, reply_aao, strlen(reply_aao)))
gameid = 15; 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))) else if (!pkt_memcmp(pkt, pos2, reply_bfv1, strlen(reply_bfv1)))
gameid = 35; gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_bfv2, strlen(reply_bfv2))) else if (!pkt_memcmp(pkt, pos2, reply_bfv2, strlen(reply_bfv2)))
gameid = 35; gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe))) else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe)))
gameid = 35; gameid = 35;
@ -216,7 +223,7 @@ static int parse(struct net_pkt *pkt)
{ {
struct net_pkt *pkt2; struct net_pkt *pkt2;
int gameid, pos, offset, queryid, subid, retval; int gameid, pos, offset, queryid, subid, retval;
if (!(gameid = pkt_check_portarr(pkt, port_arr))) if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT; 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) if ((offset = pkt_memmem(pkt, 0, search_queryid, strlen(search_queryid))) == -1)
return PARSE_REJECT; return PARSE_REJECT;
pos = offset + strlen(search_queryid); pos = offset + strlen(search_queryid);
if ((offset = pkt_parse_int(pkt, pos, &queryid)) == 0) if ((offset = pkt_parse_int(pkt, pos, &queryid)) == 0)
return PARSE_REJECT; return PARSE_REJECT;
pos += offset +1; pos += offset +1;
if ((offset = pkt_parse_int(pkt, pos, &subid)) == 0) if ((offset = pkt_parse_int(pkt, pos, &subid)) == 0)
return PARSE_REJECT; return PARSE_REJECT;
/* multipaket antworten zusammenfassen /* multipaket antworten zusammenfassen
* wenn paket non-final, dann einfach annehmen * wenn paket non-final, dann einfach annehmen
*/ */
@ -242,7 +249,7 @@ static int parse(struct net_pkt *pkt)
return PARSE_ACCEPT_FREED; return PARSE_ACCEPT_FREED;
retval = parse_real(pkt2, gameid); retval = parse_real(pkt2, gameid);
/* free merged packet */ /* free merged packet */
if (pkt != pkt2) if (pkt != pkt2)
free(pkt2); free(pkt2);
@ -250,23 +257,38 @@ static int parse(struct net_pkt *pkt)
return retval; return retval;
} }
static int gc(int timeout) { static int gc(void *privdata) {
struct gs1_part *part, *tmp;
unsigned int timeout = (int)privdata;
unsigned long now = time(NULL); unsigned long now = time(NULL);
struct gs1_part *part, *tmp;
list_for_each_entry_safe(part, tmp, &gs1_partlist, list) { list_for_each_entry_safe(part, tmp, &gs1_partlist, list) {
if (part->timeout + timeout < now) { if (part->timeout + timeout < now) {
log_print("gs1: removing dead fragment"); log_print(LOG_INFO, "gs1: removing dead fragment");
list_del(&part->list); list_del(&part->list);
free(part->pkt); free(part->pkt);
free(part); 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 = { struct hlswmaster_plugin plugin = {
.name = "gamespy1", .name = "gamespy1",
.scan = &scan, .scan = &scan,
.parse = &parse, .parse = &parse,
.gc = &gc, .init = &init,
}; };

View File

@ -18,6 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <string.h> #include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
static struct scan_ports port_arr[] = { 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))) if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT; return PARSE_REJECT;
if (pkt_memcmp(pkt, 0, replymsg, sizeof(replymsg))) if (pkt_memcmp(pkt, 0, replymsg, sizeof(replymsg)))
return PARSE_REJECT; return PARSE_REJECT;
@ -61,7 +63,7 @@ static int parse(struct net_pkt *pkt)
pkt_parse_int(pkt, pos3 + strlen(search_hostport) +1, &port); pkt_parse_int(pkt, pos3 + strlen(search_hostport) +1, &port);
switch (gameid) { switch (gameid) {
case 30:/* halo */ case 30:/* halo */
case 37:/* painkiller */ case 37:/* painkiller */
break; break;
@ -71,7 +73,7 @@ static int parse(struct net_pkt *pkt)
else else
return PARSE_REJECT; return PARSE_REJECT;
break; break;
case 45:/* battlefield 2 */ case 45:/* battlefield 2 */
// todo: pos3 check noetig? // todo: pos3 check noetig?
if (!pkt_memcmp(pkt, pos1, reply_bf2, strlen(reply_bf2)) && pos3 != -1) if (!pkt_memcmp(pkt, pos1, reply_bf2, strlen(reply_bf2)) && pos3 != -1)
@ -79,12 +81,12 @@ static int parse(struct net_pkt *pkt)
else else
return PARSE_REJECT; return PARSE_REJECT;
break; break;
default: default:
return PARSE_REJECT; return PARSE_REJECT;
break; break;
} }
/* /*
* wenn ein hostport angegeben wurde, und das nicht der src port ist * wenn ein hostport angegeben wurde, und das nicht der src port ist
* beide ports in die serverliste uebernehmen * beide ports in die serverliste uebernehmen
@ -94,7 +96,7 @@ static int parse(struct net_pkt *pkt)
} else { } else {
server_add_pkt(gameid, pkt); server_add_pkt(gameid, pkt);
} }
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }

View File

@ -17,16 +17,25 @@
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "plugin_helper.h"
struct net_entry { #include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#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 ip;
struct in_addr mask; struct in_addr netmask;
}; };
static struct net_entry *net_arr = NULL; static LIST_HEAD(validnet_list);
static int net_arr_size = 0;
static struct scan_ports port_arr[] = { static struct scan_ports port_arr[] = {
{ 27015, 27024, 1 }, /* cs/hl */ { 27015, 27024, 1 }, /* cs/hl */
@ -48,7 +57,7 @@ static int scan(void)
static int parse(struct net_pkt *pkt) static int parse(struct net_pkt *pkt)
{ {
struct in_addr tmp; struct in_addr tmp;
int port, count, pos, i; int port, count, pos;
if (!pkt_check_portarr(pkt, port_arr)) if (!pkt_check_portarr(pkt, port_arr))
return PARSE_REJECT; return PARSE_REJECT;
@ -62,13 +71,13 @@ static int parse(struct net_pkt *pkt)
server_add_pkt(1, pkt); server_add_pkt(1, pkt);
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }
/* second query?! */ /* second query?! */
if (pkt->size >= 5 && pkt->buf[4] == 'I' && pkt->buf[5] == 0x07) { if (pkt->size >= 5 && pkt->buf[4] == 'I' && pkt->buf[5] == 0x07) {
server_add_pkt(40, pkt); server_add_pkt(40, pkt);
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }
/* parse server IP */ /* parse server IP */
pos = 5; pos = 5;
if ((count = pkt_parse_ip(pkt, pos, &tmp)) == 0) if ((count = pkt_parse_ip(pkt, pos, &tmp)) == 0)
@ -80,8 +89,9 @@ static int parse(struct net_pkt *pkt)
return PARSE_REJECT; return PARSE_REJECT;
/* check server IP */ /* check server IP */
for (i = 0; i < net_arr_size; i++) { struct validnet_entry *entry;
if (((net_arr[i].ip.s_addr ^ tmp.s_addr) & net_arr[i].mask.s_addr) == 0) { 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); server_add(1, tmp.s_addr, port, 0);
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }
@ -91,86 +101,74 @@ static int parse(struct net_pkt *pkt)
return PARSE_ACCEPT; 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 */ /* valid_net x.x.x.x */
if ((p = strchr(buf, '/')) == NULL) { if (p == NULL) {
/* ip */ /* single ip */
if (inet_pton(AF_INET, buf, &net->ip) <= 0) if (inet_pton(AF_INET, buf, ip) > 0) {
return 0; netmask->s_addr = 0xFFFFFFFF;
/* 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) {
retval = 0; retval = 0;
}
/* x.x.x.x/x.x.x.x */ /* valid_net x.x.x.x/x.x.x.x or x.x.x.x/xx */
} else if (inet_pton(AF_INET, p+1, &net->mask) > 0) { } else {
retval = 1; *p = 0x00;
/* 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;
} 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; 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 = '/'; *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; struct validnet_entry *entry = malloc(sizeof(struct validnet_entry));
static char buf[32]; if (entry == NULL) {
int i = 0, j; log_print(LOG_ERROR, "halflife_init(): out of memory");
return -1;
list_for_each_entry(tupel, &section->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, &section->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);
}
} }
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) config_get_strings("halflife", "valid_net", init_callback, NULL);
free(net_arr); return 0;
} }
struct hlswmaster_plugin plugin = { struct hlswmaster_plugin plugin = {
@ -178,5 +176,4 @@ struct hlswmaster_plugin plugin = {
.scan = &scan, .scan = &scan,
.parse = &parse, .parse = &parse,
.init = &init, .init = &init,
.fini = &fini,
}; };

View File

@ -17,82 +17,89 @@
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <inttypes.h> #include <inttypes.h>
#include "plugin_helper.h" #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
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; uint16_t gameid;
uint32_t ip; uint32_t ip;
uint16_t port1; uint16_t port1;
uint16_t port2; uint16_t port2;
} __attribute__ ((packed)); } __attribute__ ((packed));
static struct in_addr *ip_arr = NULL; struct master_entry {
static int ip_arr_size = 0; struct list_head list;
struct sockaddr_in addr;
};
static LIST_HEAD(master_list);
static char scanmsg[] = "\xff\xff\xff\xffHLSWLANSEARCH"; static char scanmsg[] = "\xff\xff\xff\xffHLSWLANSEARCH";
static int scan(void) static int scan(void)
{ {
int i; struct master_entry *entry;
for (i = 0; i < ip_arr_size; i++) list_for_each_entry(entry, &master_list, list)
pkt_send(&ip_arr[i], 7140, scanmsg, sizeof(scanmsg)); pkt_send(&entry->addr.sin_addr, ntohs(entry->addr.sin_port), scanmsg, sizeof(scanmsg));
return 1; return 1;
} }
static int parse(struct net_pkt *pkt) static int parse(struct net_pkt *pkt)
{ {
struct hlsw_entry *server; // TODO: check against master_list
if (pkt_getport(pkt) != 7140) if (pkt_getport(pkt) != 7140)
return PARSE_REJECT; 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)) { while ((void *)game < ((void *)pkt->buf + pkt->size)) {
server_add(server->gameid, server->ip, server->port1, server->port2); server_add(game->gameid, game->ip, game->port1, game->port2);
server++; game++;
} }
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }
static int init(struct conf_section *section) static int init_callback(const char *value, void *privdata)
{ {
struct conf_tupel *tupel; struct master_entry *entry = malloc(sizeof(struct master_entry));
int i = 0; if (entry == NULL) {
list_for_each_entry(tupel, &section->tupel, list) log_print(LOG_ERROR, "hlswproxy_init(): out of memory");
if (!strcmp(tupel->option, "scan_ip")) return -1;
i++;
if (i == 0 || !(ip_arr = malloc(sizeof(struct in_addr) * i)))
return 0;
i = 0;
list_for_each_entry(tupel, &section->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++;
}
}
} }
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) if (config_get_strings("hlswproxy", "scan", init_callback, NULL) <= 0)
free(ip_arr); return -1;
return 0;
} }
struct hlswmaster_plugin plugin = { struct hlswmaster_plugin plugin = {
@ -100,5 +107,4 @@ struct hlswmaster_plugin plugin = {
.scan = &scan, .scan = &scan,
.parse = &parse, .parse = &parse,
.init = &init, .init = &init,
.fini = &fini,
}; };

View File

@ -18,6 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <string.h> #include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
static struct scan_ports port_arr[] = { static struct scan_ports port_arr[] = {
@ -51,7 +53,7 @@ static int scan(void)
static int parse(struct net_pkt *pkt) static int parse(struct net_pkt *pkt)
{ {
int gameid = 0, pos1, pos2; int gameid = 0, pos1, pos2;
if (!pkt_check_portarr(pkt, port_arr)) if (!pkt_check_portarr(pkt, port_arr))
return PARSE_REJECT; return PARSE_REJECT;
@ -65,38 +67,38 @@ static int parse(struct net_pkt *pkt)
pos1 += strlen(search_version); pos1 += strlen(search_version);
if (!pkt_memcmp(pkt, pos1, reply_q3, strlen(reply_q3))) if (!pkt_memcmp(pkt, pos1, reply_q3, strlen(reply_q3)))
gameid = 6; gameid = 6;
else if (!pkt_memcmp(pkt, pos1, reply_ef, strlen(reply_ef))) else if (!pkt_memcmp(pkt, pos1, reply_ef, strlen(reply_ef)))
gameid = 7; gameid = 7;
else if (!pkt_memcmp(pkt, pos1, reply_rtcw, strlen(reply_rtcw))) else if (!pkt_memcmp(pkt, pos1, reply_rtcw, strlen(reply_rtcw)))
gameid = 8; gameid = 8;
else if (!pkt_memcmp(pkt, pos1, reply_et, strlen(reply_et))) else if (!pkt_memcmp(pkt, pos1, reply_et, strlen(reply_et)))
gameid = 25; gameid = 25;
else if (!pkt_memcmp(pkt, pos1, reply_jk2, strlen(reply_jk2))) else if (!pkt_memcmp(pkt, pos1, reply_jk2, strlen(reply_jk2)))
gameid = 12; gameid = 12;
else if (!pkt_memcmp(pkt, pos1, reply_jk3, strlen(reply_jk3))) else if (!pkt_memcmp(pkt, pos1, reply_jk3, strlen(reply_jk3)))
gameid = 27; gameid = 27;
} }
if (gameid == 0 && pos2 != -1) { if (gameid == 0 && pos2 != -1) {
pos2 += strlen(search_gamename); pos2 += strlen(search_gamename);
if (!pkt_memcmp(pkt, pos2, reply_cod, strlen(reply_cod))) if (!pkt_memcmp(pkt, pos2, reply_cod, strlen(reply_cod)))
gameid = 31; gameid = 31;
else if (!pkt_memcmp(pkt, pos2, reply_coduo, strlen(reply_coduo))) else if (!pkt_memcmp(pkt, pos2, reply_coduo, strlen(reply_coduo)))
gameid = 42; gameid = 42;
else if (!pkt_memcmp(pkt, pos2, reply_cod2, strlen(reply_cod2))) else if (!pkt_memcmp(pkt, pos2, reply_cod2, strlen(reply_cod2)))
gameid = 48; gameid = 48;
} }
if (gameid == 0) if (gameid == 0)
return PARSE_REJECT; return PARSE_REJECT;
server_add_pkt(gameid, pkt); server_add_pkt(gameid, pkt);
return PARSE_ACCEPT; return PARSE_ACCEPT;
} }

View File

@ -18,7 +18,10 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <string.h> #include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
#include "scanner.h"
static char scanmsg[] = "\xff\xff\xff\xffinfo 34"; /* q2(3) */ static char scanmsg[] = "\xff\xff\xff\xffinfo 34"; /* q2(3) */
static char replymsg[] = "\xff\xff\xff\xffinfo"; static char replymsg[] = "\xff\xff\xff\xffinfo";
@ -33,7 +36,7 @@ static int parse(struct net_pkt *pkt)
{ {
if (pkt_getport(pkt) != 27910) if (pkt_getport(pkt) != 27910)
return PARSE_REJECT; return PARSE_REJECT;
if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg)))
return PARSE_REJECT; return PARSE_REJECT;

View File

@ -18,6 +18,9 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <string.h> #include <string.h>
#include "netpkt.h"
#include "scanner.h"
#include "plugin.h"
#include "plugin_helper.h" #include "plugin_helper.h"
static char scanmsg1[] = "\x80\x00\x00\x00\x00"; static char scanmsg1[] = "\x80\x00\x00\x00\x00";

193
plugin.c
View File

@ -17,174 +17,97 @@
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <pthread.h>
#include "hlswmaster.h"
#include "plugin.h" #include "plugin.h"
#include "configfile.h" #include "configfile.h"
#include "logging.h"
#include "netpkt.h" #include "netpkt.h"
#include "list.h" #include "list.h"
#include "event.h"
#define BUFSIZE 512
/** liste der geladenen plugins */
static LIST_HEAD(plugin_list); static LIST_HEAD(plugin_list);
/** sichert die plugin liste UND die plugins selbst ab */ static int plugin_load(const char *filename, void *privdata)
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)
{ {
struct hlswmaster_plugin *plugin; char *plugin_dir = (char *)privdata;
void *tmp;
struct conf_section *section; 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(); dlerror();
void *dlref = dlopen(fullname, RTLD_NOW);
/* shared objekt oeffnen */ if (dlref == NULL) {
if ((tmp = dlopen(name, RTLD_NOW))) { log_print(LOG_WARN, "plugin_load: %s", dlerror());
free(fullname);
/* plugin struct suchen */ return -1;
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);
} }
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, &section->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) int plugins_scan(void)
{ {
struct hlswmaster_plugin *plugin; struct hlswmaster_plugin *plugin;
pthread_mutex_lock(&plugin_lock);
list_for_each_entry(plugin, &plugin_list, list) list_for_each_entry(plugin, &plugin_list, list)
/* wenn vorhanden die scan funktion aufrufen */
if (plugin->scan && !plugin->scan()) 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 0;
return 1;
} }
/**
* 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) int plugins_parse(struct net_pkt *pkt)
{ {
struct hlswmaster_plugin *plugin;
int retval; int retval;
pthread_mutex_lock(&plugin_lock); struct hlswmaster_plugin *plugin;
list_for_each_entry(plugin, &plugin_list, list) { list_for_each_entry(plugin, &plugin_list, list)
/* wenn vorhanden die parse funktion aufrufen */ if (plugin->parse && (retval = plugin->parse(pkt)))
if (plugin->parse && (retval = plugin->parse(pkt))) {
pthread_mutex_unlock(&plugin_lock);
return retval; return retval;
}
}
pthread_mutex_unlock(&plugin_lock);
return PARSE_REJECT; return PARSE_REJECT;
} }
/** int plugin_init(void)
* plugins_gc()
* ruft die gc() Funktionen der Plugins auf
*
* @param timeout timeout interval in sekunden
* @return true
*/
int plugins_gc(unsigned long timeout)
{ {
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); log_print(LOG_INFO, "%d plugins loaded", cnt);
list_for_each_entry(plugin, &plugin_list, list) return 0;
if (plugin->gc)
plugin->gc(timeout);
pthread_mutex_unlock(&plugin_lock);
return 1;
} }

View File

@ -16,15 +16,16 @@
struct hlswmaster_plugin { struct hlswmaster_plugin {
struct list_head list; struct list_head list;
char name[32]; char name[32];
void *dlref;
int (*init)(void);
int (*init)(struct conf_section *config);
int (*fini)(void); int (*fini)(void);
int (*scan)(void); int (*scan)(void);
int (*parse)(struct net_pkt *pkt); 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 */ #endif /* _PLUGIN_H */

View File

@ -20,55 +20,50 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#define __USE_GNU
#include <string.h> #include <string.h>
#include <ctype.h> #include "gamelist.h"
#include "logging.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "hlswmaster.h"
#include "netpkt.h" #include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.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) int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *buf, unsigned int size)
{ {
unsigned short port; unsigned short port;
int ret = 1; int ret = 0;
while (portarr && portarr->portlo) { while (portarr && portarr->portlo) {
for (port = portarr->portlo; port <= portarr->porthi; port++) for (port = portarr->portlo; port <= portarr->porthi; port++)
if (!pkt_send(dstip, port, buf, size)) if (pkt_send(dstip, port, buf, size) < 0)
ret = 0; ret = -1;
portarr++; portarr++;
} }
return ret; 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) int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr)
{ {
unsigned short port; unsigned short port;
@ -81,225 +76,3 @@ int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr)
} }
return 0; 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;
}

View File

@ -1,35 +1,15 @@
#ifndef _PLUGIN_HELPER_H #ifndef _PLUGIN_HELPER_H
#define _PLUGIN_HELPER_H #define _PLUGIN_HELPER_H
#include <inttypes.h>
#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 { struct scan_ports {
unsigned short portlo; unsigned short portlo;
unsigned short porthi; unsigned short porthi;
unsigned short gameid; unsigned short gameid;
}; };
extern int pkt_send(struct in_addr *dstip, unsigned int dstport, char *buf, unsigned int size); int server_add(uint16_t gameid, uint32_t ip, uint16_t port1, uint16_t port2);
extern int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *buf, unsigned int size); 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);
extern int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr); 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);
#endif /* _PLUGIN_HELPER_H */ #endif /* _PLUGIN_HELPER_H */

442
scanner.c
View File

@ -21,381 +21,153 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <inttypes.h>
#include "list.h" #include "event.h"
#include "netpkt.h" #include "netpkt.h"
#include "configfile.h" #include "configfile.h"
#include "hlswmaster.h"
#include "plugin.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; 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) int pkt_send(struct in_addr *dstip, unsigned int dstport, char *buf, unsigned int size)
{ {
struct sockaddr_in addr; struct net_pkt *pkt = malloc(sizeof(struct net_pkt) + size);
int ret = 1; if (pkt == NULL) {
log_print(LOG_WARN, "pkt_send(): out of memory");
addr.sin_family = AF_INET; return -1;
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;
} }
usleep(10000); pkt->addr.sin_family = AF_INET;
return ret; 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;
} }
/** static int scanner_transmit(void *privdata)
* scan_receive_real()
* arbeitet die receive queue ab
*/
void scan_receive_real(void)
{ {
struct rx_entry *rx; if (list_empty(&tx_queue))
return -1;
struct net_pkt *pkt; struct net_pkt *pkt;
int cnt, retval; pkt = list_entry(tx_queue.next, struct net_pkt, list);
list_del(&pkt->list);
do { int ret = sendto(scan_sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&pkt->addr, sizeof(pkt->addr));
sem_wait(&rxlist_sem); if (ret <= 0)
log_print(LOG_WARN, "scanner_transmit(): sendto()");
pthread_mutex_lock(&rxlist_lock);
rx = list_entry(rxlist.next, struct rx_entry, list);
list_del(&rx->list);
pthread_mutex_unlock(&rxlist_lock);
pkt = rx->pkt; return 0;
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);
} }
/** static int scanner_scan(void *privdata)
* 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)
{ {
long last_plugin_gc = 0, last_list_refresh = 0, last_scan = 0; plugins_scan();
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);
log_print("thread_start: scan_control"); struct timeval tv;
log_print(" scan_interval: %ds", scan_interval); tv.tv_sec = 0;
log_print(" serverlist_refresh: %ds", list_refresh); tv.tv_usec = 10000;
log_print(" serverlist_timeout: %ds", list_timeout);
log_print(" plugin_timeout: %d", plugin_timeout);
while (1) { event_add_timeout(&tv, scanner_transmit, NULL);
now = time(NULL); return 0;
/* 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);
}
} }
/** static int scanner_receive(int fd, void *privdata)
* scan_receive()
* wartet auf serverantworten und uebergibt die pakete der
* receive queue
*/
void scan_receive(void)
{ {
struct net_pkt *pkt; int recvsize;
struct rx_entry *rx; if (ioctl(fd, FIONREAD, &recvsize) == -1) {
fd_set fdsel, fdcpy; log_print(LOG_WARN, "scanner_receive(): ioctl(FIONREAD)");
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()");
return 0; return 0;
} }
ip = config_get_string("global", "scan_ip", "0.0.0.0"); struct net_pkt *pkt = malloc(sizeof(struct net_pkt) + recvsize);
port = config_get_int("global", "scan_port", 7130); if (pkt == NULL) {
log_print(LOG_WARN, "scanner_receive(): out of memory");
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()");
return 0; return 0;
} }
if (setsockopt(scan_sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))) { int i = sizeof(struct sockaddr_in);
log_print("scan_init(): setsockopt()"); 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; return 0;
} }
if (atexit(scan_close) != 0) { switch (plugins_parse(pkt)) {
log_print("scan_init(): atexit()"); case PARSE_REJECT:
return 0; 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); return 0;
}
sem_init(&rxlist_sem, 0, 0);
int scanner_init()
return 1; {
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;
} }

7
scanner.h Normal file
View File

@ -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_ */

138
server.c Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#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;
}

6
server.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _SERVER_H_
#define _SERVER_H_
int server_init();
#endif /* _SERVER_H_ */