Browse Source

rewrite

master
Olaf Rempel 14 years ago
parent
commit
ce59a8853e
34 changed files with 1538 additions and 1502 deletions
  1. +35
    -0
      Makefile
  2. +0
    -196
      client.c
  3. +0
    -263
      config.c
  4. +230
    -0
      configfile.c
  5. +18
    -0
      configfile.h
  6. +310
    -0
      event.c
  7. +26
    -0
      event.h
  8. +85
    -0
      gamelist.c
  9. +25
    -0
      gamelist.h
  10. +24
    -22
      hlswmaster.c
  11. +28
    -28
      hlswmaster.conf
  12. +0
    -42
      hlswmaster.h
  13. +3
    -3
      list.h
  14. +45
    -49
      logging.c
  15. +16
    -0
      logging.h
  16. +13
    -13
      masterquery.c
  17. +143
    -0
      netpkt.c
  18. +14
    -0
      netpkt.h
  19. +3
    -1
      p_d3engine.c
  20. +39
    -17
      p_gamespy1.c
  21. +8
    -6
      p_gamespy2.c
  22. +68
    -71
      p_halflife.c
  23. +47
    -41
      p_hlswproxy.c
  24. +10
    -8
      p_q3engine.c
  25. +4
    -1
      p_quake2.c
  26. +3
    -0
      p_ut2k4.c
  27. +53
    -130
      plugin.c
  28. +6
    -5
      plugin.h
  29. +27
    -254
      plugin_helper.c
  30. +4
    -24
      plugin_helper.h
  31. +100
    -328
      scanner.c
  32. +7
    -0
      scanner.h
  33. +138
    -0
      server.c
  34. +6
    -0
      server.h

+ 35
- 0
Makefile 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)

+ 0
- 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);
}
}

+ 0
- 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
- 0
configfile.c 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
- 0
configfile.h 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
- 0
event.c 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
- 0
event.h 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
- 0
gamelist.c 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
- 0
gamelist.h 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_ */

main.c → hlswmaster.c View File

@@ -21,13 +21,18 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <getopt.h>
#include <sys/stat.h>
#include <pwd.h>

#include "hlswmaster.h"
#include "configfile.h"
#include "event.h"
#include "logging.h"

#include "gamelist.h"
#include "scanner.h"
#include "server.h"
#include "plugin.h"

static struct option opts[] = {
{"config", 1, 0, 'c'},
@@ -39,8 +44,6 @@ static struct option opts[] = {

int main(int argc, char *argv[])
{
pthread_t thread1, thread2, thread3;
pthread_attr_t attr;
int arg = 0, code = 0, debug = 0;
char *config = NULL, *user = NULL, *logfile;

@@ -84,22 +87,22 @@ int main(int argc, char *argv[])
if (user) {
struct passwd *pwl;
if (!(pwl = getpwnam(user))) {
log_print("unknown user: %s", user);
log_print(LOG_ERROR, "unknown user: %s", user);
exit(-1);
}

if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) {
log_print("setgid/setuid");
log_print(LOG_ERROR, "setgid/setuid");
exit(-1);
}
}

/* parse config file */
if (!config_parse(config))
if (config_parse(config) == -1)
exit(-1);

/* check logfile */
logfile = config_get_string("global", "logfile", NULL);
logfile = config_get_string("global", "logfile", "hlswmaster.log");
if (logfile && !debug) {
/* start logging */
if (!log_init(logfile))
@@ -109,26 +112,25 @@ int main(int argc, char *argv[])
daemon(-1, 0);
}

log_print("hlswmaster started (user: %s, pid: %d)", getpwuid(getuid())->pw_name, getpid());
log_print(LOG_INFO, "hlswmaster started (user: %s, pid: %d)", getpwuid(getuid())->pw_name, getpid());

/* init scan engine */
if (!scan_init())
/* init plugins */
if (plugin_init() == -1)
exit(-1);

/* load plugins */
plugin_load_all();
/* init gamelist */
if (gamelist_init() == -1)
exit(-1);

/* startup threads */
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 65536);
/* init server */
if (server_init() == -1)
exit(-1);

pthread_create(&thread1, &attr, (void *)&scan_control, NULL);
pthread_create(&thread2, &attr, (void *)&scan_receive, NULL);
pthread_create(&thread3, &attr, (void *)&client_handler, NULL);
/* init scanner */
if (scanner_init() == -1)
exit(-1);

/* wait untill d00msday */
while (1)
sleep(3600);
event_loop();

return 0;
}

+ 28
- 28
hlswmaster.conf View File

@@ -1,43 +1,43 @@
[global]
# broadcast scan source IP & PORT (udp)
scan_ip 0.0.0.0
scan_port 7130
## broadcast scan source IP & PORT (udp)
#scanner_src 0.0.0.0:7130

# broadcast scan every X seconds
scan_interval 60
## broadcast scan every X seconds
#scan_interval 60

# serverlist rebuild every X seconds
serverlist_interval 5
## serverlist rebuild every X seconds
#serverlist_refresh 5

# server timeout after X seconds
serverlist_timeout 180
## server timeout after X seconds
#serverlist_timeout 180

# plugin data timeout every X seconds
plugin_timeout 90
## master answers with this source IP
#master_src 0.0.0.0:7140

# master answers with this source IP
master_ip 0.0.0.0
#plugin_dir .
plugin p_d3engine.so
plugin p_gamespy1.so
plugin p_gamespy2.so
plugin p_halflife.so
plugin p_hlswproxy.so
plugin p_q3engine.so
plugin p_quake2.so
plugin p_ut2k4.so

# load these plugins
#plugin plugins/.libs/hlswproxy.so
plugin plugins/.libs/halflife.so
plugin plugins/.libs/q3engine.so
plugin plugins/.libs/quake2.so
plugin plugins/.libs/gamespy1.so
plugin plugins/.libs/gamespy2.so
plugin plugins/.libs/d3engine.so
plugin plugins/.libs/ut2k4.so
## logging
#logfile hlswmaster.log

# logging
logfile hlswmaster.log
[gamespy1]
## internal fragment-list timeout
#gc_timeout 90

[hlswproxy]
# ask these hlswmasters
scan_ip 10.10.0.1
scan_ip 10.10.0.2
## ask these hlswmasters
scan 10.10.0.1:7140
scan 10.10.0.2:7140

[halflife]
# allow these nets
## allow these nets
valid_net 10.10.0.0/16
valid_net 172.16.0.0/16


+ 0
- 42
hlswmaster.h 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 */

+ 3
- 3
list.h View File

@@ -1,5 +1,5 @@
#ifndef _LIST_H
#define _LIST_H
#ifndef _LIST_H_
#define _LIST_H_

/*
* stolen from linux kernel 2.6.11 (http://kernel.org/)
@@ -265,4 +265,4 @@ static inline void list_splice_init(struct list_head *list,
(type)__j; \
})

#endif /* _LIST_H */
#endif /* _LIST_H_ */

+ 45
- 49
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 *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -24,84 +24,80 @@
#include <errno.h>
#include <string.h>

#include "configfile.h"
#include "logging.h"

#define BUFSIZE 8192

static FILE *log_fd = NULL;
static char *buffer = NULL;

/**
* log_print()
* schreibt eine Zeile ins Logfile oder auf stderr
* wenn errno gesetzt ist, wird der error-string mit ausgegeben
*
* @param fmt variable argumentenliste (syntax wie bei printf)
*
* @todo code vereinfachen?
*/
void log_print(const char *fmt, ...)
void log_print(int prio, const char *fmt, ...)
{
va_list az;
char *buffer = malloc(8192);
int len;

if (buffer == NULL) {
buffer = malloc(BUFSIZE);
if (buffer == NULL) {
fprintf(stderr, "log_print: out of memory\nBailing out!\n");
exit(-1);
}
}

va_start(az, fmt);
vsprintf(buffer, fmt, az);
len = vsnprintf(buffer, BUFSIZE, fmt, az);
va_end(az);

/* ins logfile loggen */
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) {
time_t tzgr;
char tbuf[64];
time_t tzgr;

time(&tzgr);
strftime(tbuf, 64, "%b %d %H:%M:%S :", localtime(&tzgr));
strftime(tbuf, sizeof(tbuf), "%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);
}
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);
}
fprintf(stderr, "%s\n", buffer);
}

errno = 0;
free(buffer);
}

/**
* log_close()
* schliesst das logfile
*/
static void log_close()
static void log_close(void)
{
if (buffer)
free(buffer);

fclose(log_fd);
}

/**
* log_init()
* initialisiert das logging
*
* @param logfile filename des logfiles
* @return false bei fehler
*/
int log_init(char *logfile)
{
if ((log_fd = fopen(logfile, "a" )) == NULL) {
log_print("log_open('%s'): %s", logfile);
log_fd = fopen(logfile, "a");
if (log_fd == NULL) {
log_print(LOG_ERROR, "log_open('%s'): %s", logfile);
return 0;
}

/* beim beenden log schliessen */
if (atexit(log_close) != 0) {
log_print("log_open(): atexit()");
log_print(LOG_ERROR, "log_open(): atexit()");
return 0;
}
log_print("==========================");
log_print(LOG_EVERYTIME, "==========================");
return 1;
}

+ 16
- 0
logging.h 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_ */

+ 13
- 13
masterquery.c View File

@@ -22,7 +22,7 @@ struct _entry {
static char hlswheader[] = "\xFF\xFF\xFF\xFFHLSWLANSEARCH";

static char *id2name[] = {
"Unknown", // 0
"Unknown", // 0
"Halflife",
"Quake 1",
"Quake 2",
@@ -53,7 +53,7 @@ static char *id2name[] = {
"Medal of Honor: Allied Assault Breakthrough",
"Tribes 2",
"Halo", // 30
"Call of Duty",
"Call of Duty",
"Savage: The Battle for Newerth",
"Unreal Tournament 2004",
"HLSteam",
@@ -68,8 +68,8 @@ static char *id2name[] = {
"Starwars: Battlefront (?)",
"SWAT 4",
"Battlefield 2", // 45
"???",
"Quake 4 (???)",
"(unknown)",
"Quake 4 (?)",
"Call of Duty 2"
};

@@ -86,9 +86,9 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size)

} else {
printf("received hlsw packet from: %15s:%-5d size=%d count=%d\n",
inet_ntoa(src->sin_addr), ntohs(src->sin_port), size,
inet_ntoa(src->sin_addr), ntohs(src->sin_port), size,
((size > sizeof(hlswheader)) ? (size - sizeof(hlswheader)) / sizeof(struct _entry) : 0));
if (verbose) {
server = pkt + sizeof(hlswheader);
while ((void *)server < pkt + size) {
@@ -105,7 +105,7 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size)
static int scan_init()
{
int i = 1;
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket()");
return -1;
@@ -115,7 +115,7 @@ static int scan_init()
perror("setsockopt()");
return -1;
}
return 0;
}

@@ -139,10 +139,10 @@ static int scan_receive()

FD_ZERO(&fdsel);
FD_SET(sock, &fdsel);
tv.tv_sec = 1;
tv.tv_usec = 0;
/* timeout */
while (tv.tv_sec > 0 || tv.tv_usec > 0) {

@@ -156,7 +156,7 @@ static int scan_receive()
perror("ioctl()");
return -1;
}
if (recvsize > 0) {
if (!(pkt = malloc(recvsize))) {
perror("malloc()");
@@ -202,7 +202,7 @@ int main(int argc, char *argv[])
exit(-1);
}
break;
case 'i': /* intervall */
freq = atoi(optarg);
if (freq < 1) {
@@ -214,7 +214,7 @@ int main(int argc, char *argv[])
case 'v': /* verbose */
verbose = 1;
break;
case 'h': /* help */
printf("Usage: masterquery [options]\n"
"Options: \n"


+ 143
- 0
netpkt.c 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;
}


+ 14
- 0
netpkt.h View File

@@ -5,10 +5,24 @@
#include <netinet/in.h>
#include <netinet/ip.h>

#include "list.h"

struct net_pkt {
struct list_head list;
struct sockaddr_in addr;
unsigned int size;
unsigned char buf[0];
};

int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size);
int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size);
struct net_pkt * pkt_merge(struct net_pkt *pkt1, struct net_pkt *pkt2);

char * pkt_ntoa(struct net_pkt *pkt);
unsigned short pkt_getport(struct net_pkt *pkt);
int pkt_sameaddr(struct net_pkt *pkt1, struct net_pkt *pkt2);
int pkt_parse_int(struct net_pkt *pkt, unsigned int offset, int *val);
int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip);
char * pkt_print(struct net_pkt *pkt);

#endif /* _NETPKT_H */

+ 3
- 1
p_d3engine.c View File

@@ -18,6 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h"

static struct scan_ports port_arr[] = {
@@ -41,7 +43,7 @@ static int parse(struct net_pkt *pkt)

if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT;
if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg)))
return PARSE_REJECT;



+ 39
- 17
p_gamespy1.c View File

@@ -17,7 +17,14 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "logging.h"
#include "event.h"
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h"

static struct scan_ports port_arr[] = {
@@ -71,7 +78,7 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid,
struct net_pkt *retpkt = NULL;
struct gs1_part *part, *tmp;
unsigned int i = 1, found = 0, notfound = 0;
// wenn paket non-final ist, in liste speichern
// NULL zurueckgeben
if (pkt_memmem(pkt, 0, search_final, strlen(search_final)) == -1) {
@@ -112,7 +119,7 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid,

/* merge error, eat last paket, cleanup */
if (i != subid || notfound > subid) {
log_print("gs1: merging error!");
log_print(LOG_INFO, "gs1: merging error!");
free(pkt);
return NULL;
}
@@ -123,12 +130,12 @@ static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid,
static int parse_real(struct net_pkt *pkt, int gameid)
{
int port, offset, pos1, pos2;
pos1 = pkt_memmem(pkt, 0, search_gamename, strlen(search_gamename));
pos1 += strlen(search_gamename);
pos2 = pkt_memmem(pkt, 0, search_gameid, strlen(search_gameid));
pos2 += strlen(search_gameid);
switch (gameid) {
case 5:/* unreal tournament */
if (!pkt_memcmp(pkt, pos1, reply_ut, strlen(reply_ut)))
@@ -141,7 +148,7 @@ static int parse_real(struct net_pkt *pkt, int gameid)
/* unreal tournament 2k4 */
else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4)))
gameid = 33;
/* americas army operations */
else if (!pkt_memcmp(pkt, pos1, reply_aao, strlen(reply_aao)))
gameid = 15;
@@ -156,10 +163,10 @@ static int parse_real(struct net_pkt *pkt, int gameid)

else if (!pkt_memcmp(pkt, pos2, reply_bfv1, strlen(reply_bfv1)))
gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_bfv2, strlen(reply_bfv2)))
gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe)))
gameid = 35;

@@ -216,7 +223,7 @@ static int parse(struct net_pkt *pkt)
{
struct net_pkt *pkt2;
int gameid, pos, offset, queryid, subid, retval;
if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT;

@@ -226,15 +233,15 @@ static int parse(struct net_pkt *pkt)

if ((offset = pkt_memmem(pkt, 0, search_queryid, strlen(search_queryid))) == -1)
return PARSE_REJECT;
pos = offset + strlen(search_queryid);
if ((offset = pkt_parse_int(pkt, pos, &queryid)) == 0)
return PARSE_REJECT;
pos += offset +1;
if ((offset = pkt_parse_int(pkt, pos, &subid)) == 0)
return PARSE_REJECT;
/* multipaket antworten zusammenfassen
* wenn paket non-final, dann einfach annehmen
*/
@@ -242,7 +249,7 @@ static int parse(struct net_pkt *pkt)
return PARSE_ACCEPT_FREED;

retval = parse_real(pkt2, gameid);
/* free merged packet */
if (pkt != pkt2)
free(pkt2);
@@ -250,23 +257,38 @@ static int parse(struct net_pkt *pkt)
return retval;
}

static int gc(int timeout) {
struct gs1_part *part, *tmp;
static int gc(void *privdata) {

unsigned int timeout = (int)privdata;
unsigned long now = time(NULL);

struct gs1_part *part, *tmp;
list_for_each_entry_safe(part, tmp, &gs1_partlist, list) {
if (part->timeout + timeout < now) {
log_print("gs1: removing dead fragment");
log_print(LOG_INFO, "gs1: removing dead fragment");
list_del(&part->list);
free(part->pkt);
free(part);
}
}
return 0;
}

static int init(void)
{
int timeout = config_get_int("gamespy1", "gc_timeout", 90);

struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
event_add_timeout(&tv, gc, (void *)timeout);

return 0;
}

struct hlswmaster_plugin plugin = {
.name = "gamespy1",
.scan = &scan,
.parse = &parse,
.gc = &gc,
.init = &init,
};

+ 8
- 6
p_gamespy2.c View File

@@ -18,6 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <string.h>
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h"

static struct scan_ports port_arr[] = {
@@ -48,7 +50,7 @@ static int parse(struct net_pkt *pkt)

if (!(gameid = pkt_check_portarr(pkt, port_arr)))
return PARSE_REJECT;
if (pkt_memcmp(pkt, 0, replymsg, sizeof(replymsg)))
return PARSE_REJECT;

@@ -61,7 +63,7 @@ static int parse(struct net_pkt *pkt)
pkt_parse_int(pkt, pos3 + strlen(search_hostport) +1, &port);

switch (gameid) {
case 30:/* halo */
case 30:/* halo */
case 37:/* painkiller */
break;

@@ -71,7 +73,7 @@ static int parse(struct net_pkt *pkt)
else
return PARSE_REJECT;
break;
case 45:/* battlefield 2 */
// todo: pos3 check noetig?
if (!pkt_memcmp(pkt, pos1, reply_bf2, strlen(reply_bf2)) && pos3 != -1)
@@ -79,12 +81,12 @@ static int parse(struct net_pkt *pkt)
else
return PARSE_REJECT;
break;
default:
return PARSE_REJECT;
break;
}
/*
* wenn ein hostport angegeben wurde, und das nicht der src port ist
* beide ports in die serverliste uebernehmen
@@ -94,7 +96,7 @@ static int parse(struct net_pkt *pkt)
} else {
server_add_pkt(gameid, pkt);
}
return PARSE_ACCEPT;
}