From 90d33bffbfdf9414ebdd9f16751bc57beacf2cc9 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Thu, 2 Feb 2006 16:47:20 +0100 Subject: [PATCH] Version 0.60 - added receive-queue - changed plugin_helper api from void* to uint offsets - gamespy1 multi-packet response support - q3engine plugin rewrite - added halflife plugin --- ChangeLog | 9 +- Makefile.am | 2 +- Makefile.in | 2 +- TODO | 3 - configure | 20 ++-- configure.in | 4 +- {contrib => doc}/Doxyfile | 2 +- hlswmaster.conf | 11 +- include/list.h | 4 +- include/plugin.h | 9 ++ include/plugin_helper.h | 8 +- plugins/Makefile.am | 5 +- plugins/Makefile.in | 21 +++- plugins/doom3.c | 12 +- plugins/gamespy1.c | 226 ++++++++++++++++++++++++++++++-------- plugins/gamespy2.c | 13 ++- plugins/halflife.c | 180 ++++++++++++++++++++++++++++++ plugins/hlswproxy.c | 22 ++-- plugins/q3engine.c | 101 +++++++++-------- plugins/quake2.c | 14 ++- plugins/skel.c | 26 +++-- src/client.c | 19 ++-- src/config.c | 24 ++-- src/logging.c | 12 +- src/main.c | 13 ++- src/plugin.c | 28 +++-- src/plugin_helper.c | 176 ++++++++++++++++++++++++----- src/scanner.c | 167 +++++++++++++++++++++------- tools/masterquery.c | 17 ++- 29 files changed, 881 insertions(+), 269 deletions(-) rename {contrib => doc}/Doxyfile (99%) create mode 100644 plugins/halflife.c diff --git a/ChangeLog b/ChangeLog index f5f7819..ee6b944 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ +* Sun 15 May 2005 Olaf Rempel 0.60 +- added receive-queue +- changed plugin_helper api from void* to uint offsets +- gamespy1 multi-packet response support +- q3engine plugin rewrite +- added halflife plugin + * Tue 19 Apr 2005 Olaf Rempel 0.53 -- code cleanup (mostly doxygen tags) +- code cleanup - added doxygen file * Thu 31 Mar 2005 Olaf Rempel 0.52 diff --git a/Makefile.am b/Makefile.am index f5aada3..9718cbc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,4 +2,4 @@ AUTOMAKE_OPTIONS = foreign no-dependencies SUBDIRS = src plugins tools -EXTRA_DIST = TODO autogen.sh hlswmaster.conf contrib +EXTRA_DIST = TODO autogen.sh hlswmaster.conf doc diff --git a/Makefile.in b/Makefile.in index 3d30cab..81c60e6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -174,7 +174,7 @@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ AUTOMAKE_OPTIONS = foreign no-dependencies SUBDIRS = src plugins tools -EXTRA_DIST = TODO autogen.sh hlswmaster.conf contrib +EXTRA_DIST = TODO autogen.sh hlswmaster.conf doc all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive diff --git a/TODO b/TODO index d055a5f..4348bda 100644 --- a/TODO +++ b/TODO @@ -5,13 +5,11 @@ - auf mehreren/allen interfaces gleichzeitig scannen geht das ueberhaupt? ohne root rechte? - threads ueberwachen -- threadpool fuer client-thread? - statistiken? webserver? gamespy-export? - Makefile: stripping binarys? und natuerlich: - weitere spiele finden: - Halflife Quake 1 Command & Conquer Renegade Medal of Honor: Allied Assault @@ -26,7 +24,6 @@ und natuerlich: Tribes 2 Savage: The Battle for Newerth Pain Killer - Halflife 2 Tribes Vengeance - vorhandene parser debuggen & verbessern diff --git a/configure b/configure index 520aefd..d30702a 100644 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for hlswmaster 0.53. +# Generated by GNU Autoconf 2.59 for hlswmaster 0.60. # # Report bugs to >. # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='hlswmaster' PACKAGE_TARNAME='hlswmaster' -PACKAGE_VERSION='0.53' -PACKAGE_STRING='hlswmaster 0.53' +PACKAGE_VERSION='0.60' +PACKAGE_STRING='hlswmaster 0.60' PACKAGE_BUGREPORT='Olaf Rempel ' # Factoring default headers for most tests. @@ -953,7 +953,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures hlswmaster 0.53 to adapt to many kinds of systems. +\`configure' configures hlswmaster 0.60 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1019,7 +1019,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of hlswmaster 0.53:";; + short | recursive ) echo "Configuration of hlswmaster 0.60:";; esac cat <<\_ACEOF @@ -1160,7 +1160,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -hlswmaster configure 0.53 +hlswmaster configure 0.60 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1174,7 +1174,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by hlswmaster $as_me 0.53, which was +It was created by hlswmaster $as_me 0.60, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1818,7 +1818,7 @@ fi # Define the identity of the package. PACKAGE=hlswmaster - VERSION=0.53 + VERSION=0.60 cat >>confdefs.h <<_ACEOF @@ -21419,7 +21419,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by hlswmaster $as_me 0.53, which was +This file was extended by hlswmaster $as_me 0.60, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21482,7 +21482,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -hlswmaster config.status 0.53 +hlswmaster config.status 0.60 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.in b/configure.in index 6a6ef72..d0ba004 100644 --- a/configure.in +++ b/configure.in @@ -1,8 +1,8 @@ dnl projekname, version, bugsto -AC_INIT(hlswmaster, 0.53, [Olaf Rempel ]) +AC_INIT(hlswmaster, 0.60, [Olaf Rempel ]) dnl same for automake -AM_INIT_AUTOMAKE(hlswmaster, 0.53) +AM_INIT_AUTOMAKE(hlswmaster, 0.60) dnl do not rebuild configure AM_MAINTAINER_MODE diff --git a/contrib/Doxyfile b/doc/Doxyfile similarity index 99% rename from contrib/Doxyfile rename to doc/Doxyfile index 71b37d0..74ff6ee 100644 --- a/contrib/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- PROJECT_NAME = hlswmaster PROJECT_NUMBER = 0.53 -OUTPUT_DIRECTORY = ../doc +OUTPUT_DIRECTORY = . CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = NO diff --git a/hlswmaster.conf b/hlswmaster.conf index 578c775..fbb6041 100644 --- a/hlswmaster.conf +++ b/hlswmaster.conf @@ -10,7 +10,7 @@ scan_interval 30 serverlist_interval 5 # server timeout after X seconds -serverlist_timeout 30 +serverlist_timeout 60 # plugin data timeout every X seconds plugin_timeout 30 @@ -19,7 +19,8 @@ plugin_timeout 30 master_ip 0.0.0.0 # load these plugins -plugin plugins/.libs/hlswproxy.so +#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 @@ -30,6 +31,12 @@ plugin plugins/.libs/doom3.so logfile hlswmaster.log [hlswproxy] +# ask these hlswmasters scan_ip 10.10.0.1 scan_ip 10.10.0.2 +[halflife] +# allow these nets +valid_net 10.10.0.0/16 +valid_net 172.16.0.0/16 + diff --git a/include/list.h b/include/list.h index 8373f07..facf747 100644 --- a/include/list.h +++ b/include/list.h @@ -2,13 +2,13 @@ #define _LIST_H /* - * stolen from linux kernel (2.6.11) + * stolen from linux kernel 2.6.11 (http://kernel.org/) * linux/include/linux/stddef.h (offsetoff) * linux/include/linux/kernel.h (container_of) * linux/include/linux/list.h (*list*) * linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND) * - * modified by Olaf Rempel + * modified by Olaf Rempel */ #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) diff --git a/include/plugin.h b/include/plugin.h index 007f15d..e824535 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -5,6 +5,15 @@ #include "configfile.h" #include "list.h" +// paket nicht akzeptiert, free() muss noch aufgerufen werden +#define PARSE_REJECT 0 + +// paket akzeptiert, free() muss noch aufgerufen werden +#define PARSE_ACCEPT 1 + +// paket akzeptiert, free() wurde schon, oder darf noch aufgerufen werden +#define PARSE_ACCEPT_FREED 2 + struct hlswmaster_plugin { struct list_head list; diff --git a/include/plugin_helper.h b/include/plugin_helper.h index e4f5036..eab1886 100644 --- a/include/plugin_helper.h +++ b/include/plugin_helper.h @@ -18,12 +18,16 @@ extern int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, c extern int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr); extern int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size); -extern void * pkt_memmem(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_atoi(struct net_pkt *pkt, void *p); +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 */ diff --git a/plugins/Makefile.am b/plugins/Makefile.am index c81ab37..b7a1a69 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -4,7 +4,7 @@ EXTRA_DIST = skel.c INCLUDES = -I../include -pkgdata_LTLIBRARIES = doom3.la gamespy1.la gamespy2.la hlswproxy.la q3engine.la quake2.la +pkgdata_LTLIBRARIES = doom3.la gamespy1.la gamespy2.la halflife.la hlswproxy.la q3engine.la quake2.la doom3_la_SOURCES = doom3.c doom3_la_LDFLAGS = -module -avoid-version @@ -15,6 +15,9 @@ gamespy1_la_LDFLAGS = -module -avoid-version gamespy2_la_SOURCES = gamespy2.c gamespy2_la_LDFLAGS = -module -avoid-version +halflife_la_SOURCES = halflife.c +halflife_la_LDFLAGS = -module -avoid-version + hlswproxy_la_SOURCES = hlswproxy.c hlswproxy_la_LDFLAGS = -module -avoid-version diff --git a/plugins/Makefile.in b/plugins/Makefile.in index 2247891..a58241a 100644 --- a/plugins/Makefile.in +++ b/plugins/Makefile.in @@ -14,7 +14,7 @@ @SET_MAKE@ -SOURCES = $(doom3_la_SOURCES) $(gamespy1_la_SOURCES) $(gamespy2_la_SOURCES) $(hlswproxy_la_SOURCES) $(q3engine_la_SOURCES) $(quake2_la_SOURCES) +SOURCES = $(doom3_la_SOURCES) $(gamespy1_la_SOURCES) $(gamespy2_la_SOURCES) $(halflife_la_SOURCES) $(hlswproxy_la_SOURCES) $(q3engine_la_SOURCES) $(quake2_la_SOURCES) srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -65,6 +65,9 @@ gamespy1_la_OBJECTS = $(am_gamespy1_la_OBJECTS) gamespy2_la_LIBADD = am_gamespy2_la_OBJECTS = gamespy2.lo gamespy2_la_OBJECTS = $(am_gamespy2_la_OBJECTS) +halflife_la_LIBADD = +am_halflife_la_OBJECTS = halflife.lo +halflife_la_OBJECTS = $(am_halflife_la_OBJECTS) hlswproxy_la_LIBADD = am_hlswproxy_la_OBJECTS = hlswproxy.lo hlswproxy_la_OBJECTS = $(am_hlswproxy_la_OBJECTS) @@ -86,11 +89,13 @@ CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ SOURCES = $(doom3_la_SOURCES) $(gamespy1_la_SOURCES) \ - $(gamespy2_la_SOURCES) $(hlswproxy_la_SOURCES) \ - $(q3engine_la_SOURCES) $(quake2_la_SOURCES) + $(gamespy2_la_SOURCES) $(halflife_la_SOURCES) \ + $(hlswproxy_la_SOURCES) $(q3engine_la_SOURCES) \ + $(quake2_la_SOURCES) DIST_SOURCES = $(doom3_la_SOURCES) $(gamespy1_la_SOURCES) \ - $(gamespy2_la_SOURCES) $(hlswproxy_la_SOURCES) \ - $(q3engine_la_SOURCES) $(quake2_la_SOURCES) + $(gamespy2_la_SOURCES) $(halflife_la_SOURCES) \ + $(hlswproxy_la_SOURCES) $(q3engine_la_SOURCES) \ + $(quake2_la_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -196,13 +201,15 @@ target_alias = @target_alias@ AUTOMAKE_OPTIONS = foreign no-dependencies EXTRA_DIST = skel.c INCLUDES = -I../include -pkgdata_LTLIBRARIES = doom3.la gamespy1.la gamespy2.la hlswproxy.la q3engine.la quake2.la +pkgdata_LTLIBRARIES = doom3.la gamespy1.la gamespy2.la halflife.la hlswproxy.la q3engine.la quake2.la doom3_la_SOURCES = doom3.c doom3_la_LDFLAGS = -module -avoid-version gamespy1_la_SOURCES = gamespy1.c gamespy1_la_LDFLAGS = -module -avoid-version gamespy2_la_SOURCES = gamespy2.c gamespy2_la_LDFLAGS = -module -avoid-version +halflife_la_SOURCES = halflife.c +halflife_la_LDFLAGS = -module -avoid-version hlswproxy_la_SOURCES = hlswproxy.c hlswproxy_la_LDFLAGS = -module -avoid-version q3engine_la_SOURCES = q3engine.c @@ -275,6 +282,8 @@ gamespy1.la: $(gamespy1_la_OBJECTS) $(gamespy1_la_DEPENDENCIES) $(LINK) -rpath $(pkgdatadir) $(gamespy1_la_LDFLAGS) $(gamespy1_la_OBJECTS) $(gamespy1_la_LIBADD) $(LIBS) gamespy2.la: $(gamespy2_la_OBJECTS) $(gamespy2_la_DEPENDENCIES) $(LINK) -rpath $(pkgdatadir) $(gamespy2_la_LDFLAGS) $(gamespy2_la_OBJECTS) $(gamespy2_la_LIBADD) $(LIBS) +halflife.la: $(halflife_la_OBJECTS) $(halflife_la_DEPENDENCIES) + $(LINK) -rpath $(pkgdatadir) $(halflife_la_LDFLAGS) $(halflife_la_OBJECTS) $(halflife_la_LIBADD) $(LIBS) hlswproxy.la: $(hlswproxy_la_OBJECTS) $(hlswproxy_la_DEPENDENCIES) $(LINK) -rpath $(pkgdatadir) $(hlswproxy_la_LDFLAGS) $(hlswproxy_la_OBJECTS) $(hlswproxy_la_LIBADD) $(LIBS) q3engine.la: $(q3engine_la_OBJECTS) $(q3engine_la_DEPENDENCIES) diff --git a/plugins/doom3.c b/plugins/doom3.c index c611443..0e8b088 100644 --- a/plugins/doom3.c +++ b/plugins/doom3.c @@ -28,22 +28,24 @@ static struct scan_ports port_arr[] = { static char scanmsg[] = "\xff\xffgetInfo"; static char replymsg[] = "\xff\xffinfoResponse"; -static int scan(void) { +static int scan(void) +{ pkt_send_portarr(NULL, port_arr, scanmsg, strlen(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { +static int parse(struct net_pkt *pkt) +{ int gameid; if (!(gameid = pkt_check_portarr(pkt, port_arr))) - return 0; + return PARSE_REJECT; if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) - return 0; + return PARSE_REJECT; server_add_pkt(gameid, pkt); - return 1; + return PARSE_ACCEPT; } struct hlswmaster_plugin plugin = { diff --git a/plugins/gamespy1.c b/plugins/gamespy1.c index b45d207..b66e415 100644 --- a/plugins/gamespy1.c +++ b/plugins/gamespy1.c @@ -29,89 +29,227 @@ static struct scan_ports port_arr[] = { { 0, 0, 0 } }; -static char scanmsg[] = "\\status\\"; -static char ut_reply[] = "\\gamename\\ut\\"; -static char ut2k3_reply[] = "\\gamename\\ut2\\"; -static char ut2k4_reply[] = "\\gamename\\ut2004\\"; -static char bf1942_reply[] = "\\gamename\\bfield1942\\"; -static char bfv_reply[] = "\\game_id\\bfvietnam\\"; -static char avp2_reply[] = "\\gamename\\avp2\\"; -static char igi2_reply[] = "\\gamename\\projectigi2r\\"; -static char hostport_search[] = "\\hostport\\"; +struct gs1_part { + struct list_head list; + unsigned long timeout; + unsigned int queryid; + unsigned int subid; + struct net_pkt *pkt; +}; -static int scan(void) { +static LIST_HEAD(gs1_partlist); + +static char scanmsg[] = "\\status\\"; + +static char search_queryid[] = "\\queryid\\"; +static char search_final[] = "\\final\\"; +static char search_hostport[] = "\\hostport\\"; +static char search_gamename[] = "\\gamename\\"; +static char search_gameid[] = "\\game_id\\"; + +static char reply_ut[] = "ut\\"; +static char reply_ut2k3[] = "ut2\\"; +static char reply_ut2k4[] = "ut2004\\"; +static char reply_bf1942[] = "bfield1942\\"; +static char reply_bfv[] = "bfvietnam\\"; +static char reply_poe[] = "poe\\"; +static char reply_opk[] = "opk\\"; +static char reply_avp2[] = "avp2\\"; +static char reply_igi2[] = "projectigi2r\\"; + +static int scan(void) +{ pkt_send_portarr(NULL, port_arr, scanmsg, strlen(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { - int gameid, port; - void *p; +static struct net_pkt * gs1_pkt_merge(struct net_pkt *pkt, unsigned int queryid, unsigned int subid) +{ + 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) { + part = malloc(sizeof(struct gs1_part)); + part->timeout = time(NULL); + part->queryid = queryid; + part->subid = subid; + part->pkt = pkt; - if (!(gameid = pkt_check_portarr(pkt, port_arr))) - return 0; + // in liste packen + list_add_tail(&part->list, &gs1_partlist); + return NULL; + } + // wenn paket final ist, dann mit evtl. non-final paketen mergen + // komplettes Paket zurueckgeben + while (i < subid && notfound <= subid) { + found = 0; + list_for_each_entry_safe(part, tmp, &gs1_partlist, list) { + if (pkt_sameaddr(part->pkt, pkt) && (part->queryid == queryid) && part->subid == i) { + if (retpkt != NULL) { + struct net_pkt *retpkt2 = retpkt; + retpkt = pkt_merge(retpkt, part->pkt); + free(retpkt2); + free(part->pkt); + } else { + retpkt = part->pkt; + } + list_del(&part->list); + free(part); + found = 1; + i++; + } + } + if (found == 0) + notfound++; + } + + /* merge error, eat last paket, cleanup */ + if (i != subid || notfound > subid) { + log_print("gs1: merging error!"); + free(pkt); + return NULL; + } + + return (retpkt != NULL) ? pkt_merge(retpkt, pkt) : pkt; +} + +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 2k3 */ - if (pkt_memmem(pkt, 0, ut_reply, strlen(ut_reply))) + case 5:/* unreal tournament */ + if (!pkt_memcmp(pkt, pos1, reply_ut, strlen(reply_ut))) gameid = 5; /* unreal tournament 2k3 */ - else if (pkt_memmem(pkt, 0, ut2k3_reply, strlen(ut2k3_reply))) + else if (!pkt_memcmp(pkt, pos1, reply_ut2k3, strlen(reply_ut2k4))) gameid = 14; /* unreal tournament 2k4 */ - else if (pkt_memmem(pkt, 0, ut2k4_reply, strlen(ut2k4_reply))) + else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4))) gameid = 33; - else - return 0; + return PARSE_REJECT; break; case 16:/* battlefield 1942 */ - if (!pkt_memmem(pkt, 0, bf1942_reply, strlen(bf1942_reply))) - return 0; + case 35:/* battlefield vietnam */ + if (!pkt_memcmp(pkt, pos1, reply_bf1942, strlen(reply_bf1942))) + gameid = 16; + + else if (!pkt_memcmp(pkt, pos2, reply_bfv, strlen(reply_bfv))) + gameid = 35; + + else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe))) + gameid = 35; + + else if (!pkt_memcmp(pkt, pos2, reply_opk, strlen(reply_opk))) + gameid = 35; + else + return PARSE_REJECT; break; case 17:/* alien vs. predator 2 */ - if (!pkt_memmem(pkt, 0, avp2_reply, strlen(avp2_reply))) - return 0; + if (!pkt_memcmp(pkt, pos1, reply_avp2, strlen(reply_avp2))) + gameid = 17; + else + return PARSE_REJECT; break; - + case 19:/* project igi2 covert strike */ - if (!pkt_memmem(pkt, 0, igi2_reply, strlen(igi2_reply))) - return 0; + if (!pkt_memcmp(pkt, pos1, reply_igi2, strlen(reply_igi2))) + gameid = 19; + else + return PARSE_REJECT; break; - - case 35:/* battlefield vietnam */ - if (!pkt_memmem(pkt, 0, bfv_reply, strlen(bfv_reply))) - return 0; - break; - + default: - return 0; + return PARSE_REJECT; } /* hostport angabe suchen */ - p = pkt_memmem(pkt, 0, hostport_search, strlen(hostport_search)); - if (p) { - port = pkt_atoi(pkt, p + strlen(hostport_search)); - } + offset = pkt_memmem(pkt, 0, search_hostport, strlen(search_hostport)); + if (offset != -1) + pkt_parse_int(pkt, offset + strlen(search_hostport), &port); - /* wenn ein hostport angegeben wurde, und das nicht der src port ist - ** beide ports in die serverliste uebernehmen - */ - if (p && port != ntohs(pkt->addr.sin_port)) { + /* + * wenn ein hostport angegeben wurde, und das nicht der src port ist + * beide ports in die serverliste uebernehmen + */ + if ((offset != -1) && (port != ntohs(pkt->addr.sin_port))) { server_add(gameid, pkt->addr.sin_addr.s_addr, port, ntohs(pkt->addr.sin_port)); } else { server_add_pkt(gameid, pkt); } + + return PARSE_ACCEPT; +} + +static int parse(struct net_pkt *pkt) +{ + struct net_pkt *pkt2; + int gameid, pos, offset, queryid, subid, retval; - return 1; + if (!(gameid = pkt_check_portarr(pkt, port_arr))) + return PARSE_REJECT; + + /* eat ut connection attemps */ + if (gameid == 5 && pkt->size <= 6) + return PARSE_ACCEPT; + + 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 + */ + if ((pkt2 = gs1_pkt_merge(pkt, queryid, subid)) == NULL) + return PARSE_ACCEPT_FREED; + + retval = parse_real(pkt2, gameid); + + /* free merged packet */ + if (pkt != pkt2) + free(pkt2); + + return retval; +} + +static int gc(int timeout) { + struct gs1_part *part, *tmp; + unsigned long now = time(NULL); + + list_for_each_entry_safe(part, tmp, &gs1_partlist, list) { + if (part->timeout + timeout < now) { + log_print("gs1: removing dead fragment"); + list_del(&part->list); + free(part->pkt); + free(part); + } + } } struct hlswmaster_plugin plugin = { .name = "gamespy1", .scan = &scan, .parse = &parse, + .gc = &gc, }; diff --git a/plugins/gamespy2.c b/plugins/gamespy2.c index 205e850..022565e 100644 --- a/plugins/gamespy2.c +++ b/plugins/gamespy2.c @@ -22,28 +22,31 @@ static struct scan_ports port_arr[] = { { 2302, 2302, 30 }, /* halo(30) */ + { 3455, 3455, 37 }, /* painkiller(34) */ { 0,0,0 } }; static char scanmsg[] = { 0xFE, 0xFD, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00 }; static char replymsg[] = { 0x00, 0xDE, 0xAD, 0xBE, 0xEF }; -static int scan(void) { +static int scan(void) +{ pkt_send_portarr(NULL, port_arr, scanmsg, sizeof(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { +static int parse(struct net_pkt *pkt) +{ int gameid; if (!(gameid = pkt_check_portarr(pkt, port_arr))) - return 0; + return PARSE_REJECT; if (pkt_memcmp(pkt, 0, replymsg, sizeof(replymsg))) - return 0; + return PARSE_REJECT; server_add_pkt(gameid, pkt); - return 1; + return PARSE_ACCEPT; } struct hlswmaster_plugin plugin = { diff --git a/plugins/halflife.c b/plugins/halflife.c new file mode 100644 index 0000000..9920df0 --- /dev/null +++ b/plugins/halflife.c @@ -0,0 +1,180 @@ +/*************************************************************************** + * Copyright (C) 04/2005 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include "plugin_helper.h" + +struct net_entry { + struct in_addr ip; + struct in_addr mask; +}; + +static struct net_entry *net_arr = NULL; +static int net_arr_size = 0; + +static struct scan_ports port_arr[] = { + { 27015, 27024, 1 }, /* cs/hl */ + { 0, 0, 0 } +}; + +static char scanmsg1[] = "\xff\xff\xff\xff" "details"; +static char scanmsg2[] = "\xff\xff\xff\xff\x54"; + +static int scan(void) +{ + pkt_send_portarr(NULL, port_arr, scanmsg1, strlen(scanmsg1)); + pkt_send_portarr(NULL, port_arr, scanmsg2, strlen(scanmsg2)); + return 1; +} + +static int parse(struct net_pkt *pkt) +{ + struct in_addr tmp; + int port, count, pos, i; + + if (!pkt_check_portarr(pkt, port_arr)) + return PARSE_REJECT; + + // check 0xFF 0xFF 0xFF 0xFF + if (pkt_memcmp(pkt, 0, scanmsg1, 4)) + return PARSE_REJECT; + + /* check for short answer without ip/port */ + if (pkt->size >= 5 && pkt->buf[4] == 'm' && pkt->buf[5] == 0x00) { + server_add_pkt(1, pkt); + return PARSE_ACCEPT; + } + + /* second query?! */ + if (pkt->size >= 5 && pkt->buf[4] == 'I' && pkt->buf[5] == 0x07) { + server_add_pkt(40, pkt); + return PARSE_ACCEPT; + } + + /* parse server IP */ + pos = 5; + if ((count = pkt_parse_ip(pkt, pos, &tmp)) == 0) + return PARSE_REJECT; + + /* parse server port */ + pos += count +1; + if ((count = pkt_parse_int(pkt, pos, &port)) == 0) + return PARSE_REJECT; + + /* check server IP */ + for (i = 0; i < net_arr_size; i++) { + if (((net_arr[i].ip.s_addr ^ tmp.s_addr) & net_arr[i].mask.s_addr) == 0) { + server_add(1, tmp.s_addr, port, 0); + return PARSE_ACCEPT; + } + } + + server_add(1, pkt->addr.sin_addr.s_addr, port, 0); + return PARSE_ACCEPT; +} + +static int parse_net(char *buf, struct net_entry *net) +{ + char *p; + + /* valid_net x.x.x.x */ + if ((p = strchr(buf, '/')) == NULL) { + /* ip */ + if (inet_pton(AF_INET, buf, &net->ip) <= 0) + return 0; + + /* mask */ + net->mask.s_addr = 0xFFFFFFFF; + return 1; + + /* valid_net x.x.x.x/x.x.x.x oder x.x.x.x/xx */ + } else { + int retval, mask; + *p = 0x00; + + /* ip */ + if (inet_pton(AF_INET, buf, &net->ip) <= 0) { + retval = 0; + + /* x.x.x.x/x.x.x.x */ + } else if (inet_pton(AF_INET, p+1, &net->mask) > 0) { + retval = 1; + + /* x.x.x.x/xx */ + } else { + mask = atoi(p+1); + + if (mask >= 0 && mask <= 32) { + net->mask.s_addr = htonl(0xFFFFFFFF << (32 - mask)); + retval = 1; + + } else { + retval = 0; + } + } + + *p = '/'; + return retval; + } +} + +static int init(struct conf_section *section) +{ + struct conf_tupel *tupel; + static char buf[32]; + int i = 0, j; + + list_for_each_entry(tupel, §ion->tupel, list) + if (!strcmp(tupel->option, "valid_net")) + i++; + + if (i == 0 || !(net_arr = malloc(sizeof(struct net_entry) * i))) + return 0; + + i = 0; + list_for_each_entry(tupel, §ion->tupel, list) { + if (strcmp(tupel->option, "valid_net")) + continue; + + if (parse_net(tupel->parameter, &net_arr[i])) { + j = sprintf(buf, " adding: %s", inet_ntoa(net_arr[i].ip)); + sprintf(buf + j, "/%s", inet_ntoa(net_arr[i].mask)); + log_print(buf); + i++; + } else { + log_print(" invalid net: %s", tupel->parameter); + } + } + net_arr_size = i; + return 1; +} + +static int fini() +{ + if (net_arr_size > 0 && net_arr) + free(net_arr); +} + +struct hlswmaster_plugin plugin = { + .name = "halflife", + .scan = &scan, + .parse = &parse, + .init = &init, + .fini = &fini, +}; diff --git a/plugins/hlswproxy.c b/plugins/hlswproxy.c index 79e01e8..e6cc46f 100644 --- a/plugins/hlswproxy.c +++ b/plugins/hlswproxy.c @@ -26,7 +26,7 @@ #include "plugin_helper.h" -struct _entry { +struct hlsw_entry { u_int16_t gameid; u_int32_t ip; u_int16_t port1; @@ -38,17 +38,19 @@ static int ip_arr_size = 0; static char scanmsg[] = "\xff\xff\xff\xffHLSWLANSEARCH"; -static int scan(void) { +static int scan(void) +{ int i; for (i = 0; i < ip_arr_size; i++) pkt_send(&ip_arr[i], 7140, scanmsg, sizeof(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { - struct _entry *server; +static int parse(struct net_pkt *pkt) +{ + struct hlsw_entry *server; if (pkt_getport(pkt) != 7140) - return 0; + return PARSE_REJECT; server = (void *)pkt->buf + sizeof(scanmsg); @@ -56,10 +58,12 @@ static int parse(struct net_pkt *pkt) { server_add(server->gameid, server->ip, server->port1, server->port2); server++; } - return 1; + + return PARSE_ACCEPT; } -static int init(struct conf_section *section) { +static int init(struct conf_section *section) +{ struct conf_tupel *tupel; int i = 0; list_for_each_entry(tupel, §ion->tupel, list) @@ -81,11 +85,11 @@ static int init(struct conf_section *section) { } } ip_arr_size = i; - log_print(" added %d master server(s)", i); return 1; } -static int fini() { +static int fini() +{ if (ip_arr_size > 0 && ip_arr) free(ip_arr); } diff --git a/plugins/q3engine.c b/plugins/q3engine.c index 9571966..242c245 100644 --- a/plugins/q3engine.c +++ b/plugins/q3engine.c @@ -22,82 +22,79 @@ static struct scan_ports port_arr[] = { { 27960, 27969, 6 }, /* q3(6), ef(7), et25), rtcw(8) */ - { 28070, 28070, 12 }, /* jk2(12) TODO: UNTESTED */ + { 28070, 28070, 12 }, /* jk2(12) */ { 28960, 28963, 31 }, /* cod(31), cod:uo(42) */ - { 29070, 29070, 27 }, /* jk3(27) TODO: UNTESTED */ + { 29070, 29070, 27 }, /* jk3(27) */ { 0,0,0 } }; static char scanmsg[] = "\xff\xff\xff\xffgetStatus"; static char replymsg[] = "\xff\xff\xff\xffstatusResponse"; +static char search_version[] = "\\version\\"; +static char search_gamename[] = "\\gamename\\"; +static char reply_q3[] = "Q3 "; +static char reply_ef[] = "ST:V HM "; +static char reply_rtcw[] = "Wolf "; +static char reply_et[] = "ET"; +static char reply_jk2[] = "JK2MP"; +static char reply_jk3[] = "JAmp"; +static char reply_cod[] = "Call of Duty\\"; +static char reply_coduo[] = "CoD:United Offensive\\"; -static char q3_reply[] = "\\version\\Q3 "; -static char ef_reply[] = "\\version\\ST:V HM "; -static char rtcw_reply[] = "\\version\\Wolf "; -static char et_reply[] = "\\version\\ET "; -static char jk2_reply[] = "\\version\\JK2MP "; -static char jk3_reply[] = "\\version\\JAmp "; -static char cod_reply[] = "\\gamename\\Call of Duty\\"; -static char coduo_reply[] = "\\gamename\\CoD:United Offensive\\"; - -static int scan(void) { +static int scan(void) +{ pkt_send_portarr(NULL, port_arr, scanmsg, strlen(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { - int gameid; - - if (!(gameid = pkt_check_portarr(pkt, port_arr))) - return 0; +static int parse(struct net_pkt *pkt) +{ + int gameid = 0, pos1, pos2; + if (!pkt_check_portarr(pkt, port_arr)) + return PARSE_REJECT; + if (pkt_memcmp(pkt, 0, replymsg, strlen(replymsg))) - return 0; + return PARSE_REJECT; - switch (gameid) { - case 6: /* q3, ef, et, rtcw */ - if (pkt_memmem(pkt, 0, q3_reply, strlen(q3_reply))) { + pos1 = pkt_memmem(pkt, 0, search_version, strlen(search_version)); + pos2 = pkt_memmem(pkt, 0, search_gamename, strlen(search_gamename)); + + if (pos1 != -1) { + pos1 += strlen(search_version); + if (!pkt_memcmp(pkt, pos1, reply_q3, strlen(reply_q3))) gameid = 6; - - } else if (pkt_memmem(pkt, 0, ef_reply, strlen(ef_reply))) { + + else if (!pkt_memcmp(pkt, pos1, reply_ef, strlen(reply_ef))) gameid = 7; - } else if (pkt_memmem(pkt, 0, rtcw_reply, strlen(rtcw_reply))) { + else if (!pkt_memcmp(pkt, pos1, reply_rtcw, strlen(reply_rtcw))) gameid = 8; - } else if (pkt_memmem(pkt, 0, et_reply, strlen(et_reply))) { + else if (!pkt_memcmp(pkt, pos1, reply_et, strlen(reply_et))) gameid = 25; - - } else { - return 0; - } - break; - case 12: /* jedi knight 2 */ - if (!pkt_memmem(pkt, 0, jk2_reply, strlen(jk2_reply))) - return 0; - break; - - case 27: /* jedi knight 3 */ - if (!pkt_memmem(pkt, 0, jk3_reply, strlen(jk3_reply))) - return 0; - break; - - case 31: /* cod, cod:uo */ - if (pkt_memmem(pkt, 0, cod_reply, strlen(cod_reply))) { - gameid = 31; + else if (!pkt_memcmp(pkt, pos1, reply_jk2, strlen(reply_jk2))) + gameid = 12; - } else if (pkt_memmem(pkt, 0, coduo_reply, strlen(coduo_reply))) { - gameid = 42; - - } else { - return 0; - } - break; + else if (!pkt_memcmp(pkt, pos1, reply_jk3, strlen(reply_jk3))) + gameid = 27; } - + + if (gameid == 0 && pos2 != -1) { + pos2 += strlen(search_gamename); + if (!pkt_memcmp(pkt, pos2, reply_cod, strlen(reply_cod))) + gameid = 31; + + else if (!pkt_memcmp(pkt, pos2, reply_coduo, strlen(reply_coduo))) + gameid = 42; + } + + if (gameid == 0) + return PARSE_REJECT; + server_add_pkt(gameid, pkt); - return 1; + return PARSE_ACCEPT; } struct hlswmaster_plugin plugin = { diff --git a/plugins/quake2.c b/plugins/quake2.c index f49d1bd..97c8c29 100644 --- a/plugins/quake2.c +++ b/plugins/quake2.c @@ -20,22 +20,24 @@ #include #include "plugin_helper.h" -static char scanmsg[] = "\xff\xff\xff\xffinfo 34"; +static char scanmsg[] = "\xff\xff\xff\xffinfo 34"; /* q2(3) */ -static int scan(void) { +static int scan(void) +{ pkt_send(NULL, 27910, scanmsg, strlen(scanmsg)); return 1; } -static int parse(struct net_pkt *pkt) { +static int parse(struct net_pkt *pkt) +{ if (pkt_getport(pkt) != 27910) - return 0; + return PARSE_REJECT; if (pkt_memcmp(pkt, 0, scanmsg, strlen(scanmsg))) - return 0; + return PARSE_REJECT; server_add_pkt(3, pkt); - return 1; + return PARSE_ACCEPT; } struct hlswmaster_plugin plugin = { diff --git a/plugins/skel.c b/plugins/skel.c index 2cd029e..6746e54 100644 --- a/plugins/skel.c +++ b/plugins/skel.c @@ -38,7 +38,7 @@ static int scan(void) { struct in_addr tmp; inet_aton("192.168.1.100", &tmp) pkt_send(&tmp, 8888, myscan, strlen(myscan)); - + /* an portrange(s) senden */ struct scan_ports port_arr[] = { { 27960, 27963, 0 }, /* von 27960-27963 scannen */ @@ -62,44 +62,48 @@ static int scan(void) { static int parse(struct net_pkt *pkt) { /* checken ob das paket vom richtigen port kommt: */ if (pkt_getport(pkt) != 27910) - return 0; + return PARSE_REJECT; /* bei einem port_arr checken ob es von einem der angefragten ports kommt */ int gameid; if (!(gameid = pkt_check_portarr(pkt, port_arr))) - return 0; + return PARSE_REJECT; /* gameid enthaelt nun den dritten wert einer passenden port_arr zeile */ /* paketinhalt checken (packet = "good morning dave\x00") */ char mycmp1[] = "good"; if (pkt_memcmp(pkt, 0, mycmp1, strlen(mycmp1))) - return 0; + return PARSE_REJECT; /* paketinhalt mit offset */ char mycmp2[] = "morning"; if (pkt_memcmp(pkt, 5, mycmp2, strlen(mycmp2))) - return 0; + return PARSE_REJECT; /* suchen ob paketinhalt vorhanden ist (packet = "blablablablabla\hostport\7777\blablabla") */ char mymem[] = "\\hostport\\"; void *p p = pkt_memmem(pkt, 0, mymem, strlen(mymem)); - if (!p) - return 0; + if (p == -1) + return PARSE_REJECT; /* ints aus paket parsen: */ - p += strlen(mymem); - port2 = pkt_atoi(pkt, p); - + int port2; + + if (pkt_parse_int(pkt, offset, &port2) == 0) + return PARSE_REJECT; + /* src ip des pakets */ printf("server found: %s\n", pkt_ntoa(pkt)); /* server zu interner liste hinzufuegen: (port1 = port, port2 = 0)*/ server_add_pkt(gameid, pkt); - + /* server mit zwei ports zur liste hinzufuegen: */ server_add(gameid, pkt->addr.sin_addr.s_addr, port1, port2); + + return PARSE_ACCEPT; } /** diff --git a/src/client.c b/src/client.c index 21a5a3c..faa5bc3 100644 --- a/src/client.c +++ b/src/client.c @@ -59,8 +59,9 @@ static pthread_mutex_t client_pkt_list_lock = PTHREAD_MUTEX_INITIALIZER; * @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)); +static inline int client_pkt_not_full(const struct client_pkt *cpkt, void *unused) +{ + return (cpkt->size <= (MAX_PKT_LEN - HLSW_ENTRY_LEN)); } /** @@ -70,7 +71,8 @@ static inline int client_pkt_not_full(const struct client_pkt *cpkt, void *unuse * @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) { +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); @@ -81,7 +83,7 @@ static struct client_pkt * client_pkt_add_real(struct list_head *list) { memcpy(cpkt->buf, HLSW_HEADER, HLSW_HEADER_LEN); cpkt->size = HLSW_HEADER_LEN; - /* am anfang eingliedern! */ + /* am anfang eingliedern! (suche nach freien paket schneller) */ list_add(&cpkt->list, list); } return cpkt; @@ -95,7 +97,8 @@ static struct client_pkt * client_pkt_add_real(struct list_head *list) { * @param *server pointer den server * @return false bei fehler */ -int client_pkt_add(struct game_server *server) { +int client_pkt_add(struct game_server *server) +{ struct client_pkt *cpkt; /* sucht ein freies paket, oder erzeugt eins */ @@ -117,7 +120,8 @@ int client_pkt_add(struct game_server *server) { * * @return true */ -int client_pkt_commit() { +int client_pkt_commit() +{ struct list_head old_list, *cpkt, *tmp; /* wenn die liste leer ist, nur HLSW-header verschicken */ @@ -148,7 +152,8 @@ int client_pkt_commit() { * client_handler() * empfaengt und beantwortet die HLSW anfragen der clients */ -void client_handler(void) { +void client_handler(void) +{ struct client_pkt *cpkt; struct sockaddr_in dst; int sock, i; diff --git a/src/config.c b/src/config.c index 05b930c..fefa8bc 100644 --- a/src/config.c +++ b/src/config.c @@ -40,7 +40,8 @@ static LIST_HEAD(config_list); * * @todo sectionname ist nicht eindeutig */ -static struct conf_section * config_add_section(char *name) { +static struct conf_section * config_add_section(char *name) +{ struct conf_section *section; if (!name) @@ -66,7 +67,8 @@ static struct conf_section * config_add_section(char *name) { * @param *parameter wert der option * @return false bei fehler */ -static int config_add_tupel(struct conf_section *section, char *option, char *parameter) { +static int config_add_tupel(struct conf_section *section, char *option, char *parameter) +{ struct conf_tupel *tupel; if (!section || !option || !parameter) @@ -88,7 +90,8 @@ static int config_add_tupel(struct conf_section *section, char *option, char *pa * config_free() * entfernt die config_liste aus dem Speicher */ -static void config_free() { +static void config_free() +{ struct conf_section *section, *section_tmp; struct conf_tupel *tupel, *tupel_tmp; @@ -111,7 +114,8 @@ static void config_free() { * @param *config filename des configfiles * @return false bei fehler */ -int config_parse(char *config) { +int config_parse(char *config) +{ struct conf_section *section = NULL; FILE *fz; int i = 0, ret = 1; @@ -189,7 +193,8 @@ int config_parse(char *config) { * @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 * config_get_section(char *name) +{ struct conf_section *section; list_for_each_entry(section, &config_list, list) { @@ -208,7 +213,8 @@ struct conf_section * config_get_section(char *name) { * @param *option option name * @return pointer auf den parameter string oder NULL bei fehler */ -char * config_get_parameter(struct conf_section *section, char *option) { +char * config_get_parameter(struct conf_section *section, char *option) +{ struct conf_tupel *tupel; list_for_each_entry(tupel, §ion->tupel, list) { @@ -227,7 +233,8 @@ char * config_get_parameter(struct conf_section *section, char *option) { * @param *def default string * @return parameter string oder default string bei fehler */ -char * config_get_string(char *section, char *option, char *def) { +char * config_get_string(char *section, char *option, char *def) +{ struct conf_section *tmp; char *ret; @@ -247,7 +254,8 @@ char * config_get_string(char *section, char *option, char *def) { * @param *def default wert * @return parameter wert oder default wert bei fehler */ -int config_get_int(char *section, char *option, int def) { +int config_get_int(char *section, char *option, int def) +{ char *ret; ret = config_get_string(section, option, NULL); diff --git a/src/logging.c b/src/logging.c index 3992901..1a2d6b8 100644 --- a/src/logging.c +++ b/src/logging.c @@ -37,9 +37,10 @@ static FILE *log_fd = NULL; * * @todo code vereinfachen? */ -void log_print(const char *fmt, ...) { +void log_print(const char *fmt, ...) +{ va_list az; - char buffer[256]; + char *buffer = malloc(8192); va_start(az, fmt); vsprintf(buffer, fmt, az); @@ -69,13 +70,15 @@ void log_print(const char *fmt, ...) { } } errno = 0; + free(buffer); } /** * log_close() * schliesst das logfile */ -static void log_close() { +static void log_close() +{ fclose(log_fd); } @@ -86,7 +89,8 @@ static void log_close() { * @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_print("log_open('%s'): %s", logfile); return 0; diff --git a/src/main.c b/src/main.c index a275660..1238a53 100644 --- a/src/main.c +++ b/src/main.c @@ -37,11 +37,12 @@ static struct option opts[] = { {0, 0, 0, 0} }; -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ pthread_t thread1, thread2, thread3; int arg = 0, code = 0, debug = 0; char *config = NULL, *user = NULL, *logfile; - + while (code != -1) { code = getopt_long(argc, argv, "c:u:dh", opts, &arg); @@ -85,7 +86,7 @@ int main(int argc, char *argv[]) { log_print("unknown user: %s", user); exit(-1); } - + if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) { log_print("setgid/setuid"); exit(-1); @@ -102,13 +103,13 @@ int main(int argc, char *argv[]) { /* start logging */ if (!log_init(logfile)) exit(-1); - + /* zum daemon mutieren */ daemon(-1, 0); } - + log_print("hlswmaster started (user: %s, pid: %d)", getpwuid(getuid())->pw_name, getpid()); - + /* init scan engine */ if (!scan_init()) exit(-1); diff --git a/src/plugin.c b/src/plugin.c index 1f5190b..b7b76ae 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -40,7 +40,8 @@ static pthread_mutex_t plugin_lock = PTHREAD_MUTEX_INITIALIZER; * @param *name filename des plugins * @return false on error */ -int plugin_load(char *name) { +int plugin_load(char *name) +{ struct hlswmaster_plugin *plugin; void *tmp; struct conf_section *section; @@ -82,7 +83,8 @@ int plugin_load(char *name) { * * @return false on error */ -int plugin_load_all() { +int plugin_load_all() +{ struct conf_section *section; struct conf_tupel *tupel; @@ -102,7 +104,8 @@ int plugin_load_all() { * * @return false on error */ -int plugin_unload_all() { +int plugin_unload_all() +{ struct hlswmaster_plugin *plugin, *tmp; pthread_mutex_lock(&plugin_lock); @@ -125,7 +128,8 @@ int plugin_unload_all() { * * @return true */ -int plugins_scan(void) { +int plugins_scan(void) +{ struct hlswmaster_plugin *plugin; pthread_mutex_lock(&plugin_lock); @@ -144,22 +148,25 @@ int plugins_scan(void) { * bis ein Plugin das Paket annimmt * * @param *pkt das zu parsene paket - * @return false wenn kein Plugin das Paket angenommen hat + * @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; pthread_mutex_lock(&plugin_lock); list_for_each_entry(plugin, &plugin_list, list) { /* wenn vorhanden die parse funktion aufrufen */ - if (plugin->parse && plugin->parse(pkt)) { + if (plugin->parse && (retval = plugin->parse(pkt))) { pthread_mutex_unlock(&plugin_lock); - return 1; + return retval; } } pthread_mutex_unlock(&plugin_lock); - return 0; + return PARSE_REJECT; } /** @@ -169,7 +176,8 @@ int plugins_parse(struct net_pkt *pkt) { * @param timeout timeout interval in sekunden * @return true */ -int plugins_gc(unsigned long timeout) { +int plugins_gc(unsigned long timeout) +{ struct hlswmaster_plugin *plugin; pthread_mutex_lock(&plugin_lock); diff --git a/src/plugin_helper.c b/src/plugin_helper.c index 773e7a0..368569f 100644 --- a/src/plugin_helper.c +++ b/src/plugin_helper.c @@ -46,10 +46,11 @@ * @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; int ret = 1; - + while (portarr && portarr->portlo) { for (port = portarr->portlo; port <= portarr->porthi; port++) if (!pkt_send(dstip, port, buf, size)) @@ -68,7 +69,8 @@ int pkt_send_portarr(struct in_addr *dstip, struct scan_ports *portarr, char *bu * @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; while (portarr && portarr->portlo) { for (port = portarr->portlo; port <= portarr->porthi; port++) @@ -88,12 +90,13 @@ int pkt_check_portarr(struct net_pkt *pkt, struct scan_ports *portarr) { * @param offset offset ab dem verglichen wird * @param *search daten nach denen gesucht wird * @param size laenge der daten - * @return true wenn gleich + * @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) { - +int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) +{ + if (offset >= pkt->size) return 1; @@ -112,13 +115,42 @@ int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned * @param offset offset ab dem gesucht wird * @param *search daten nach denen gesucht wird * @param size laenge der daten - * @return pointer auf den string im Paket, oder NULL wenn nicht gefunden + * @return offset auf den string im Paket, oder -1 wenn nicht gefunden */ -void * pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) { - if (offset >= pkt->size) - return NULL; +int pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) +{ + void *found; - return memmem(pkt->buf + offset, pkt->size, search, size); + 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; } /** @@ -129,7 +161,8 @@ void * pkt_memmem(struct net_pkt *pkt, unsigned int offset, char *search, unsign * @param *pkt daten vom gameserver (fuer ip/port) * @return false bei fehler */ -int server_add_pkt(unsigned int gameid, struct net_pkt *pkt) { +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); } @@ -140,7 +173,8 @@ int server_add_pkt(unsigned int gameid, struct net_pkt *pkt) { * @param *pkt daten vom gameserver * @return pointer auf String */ -char * pkt_ntoa(struct net_pkt *pkt) { +char * pkt_ntoa(struct net_pkt *pkt) +{ return inet_ntoa(pkt->addr.sin_addr); } @@ -151,31 +185,121 @@ char * pkt_ntoa(struct net_pkt *pkt) { * @param *pkt daten vom gameserver * @return portnummer */ -unsigned short pkt_getport(struct net_pkt *pkt) { +unsigned short pkt_getport(struct net_pkt *pkt) +{ return ntohs(pkt->addr.sin_port); } /** - * pkt_atoi() + * 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 *p pointer auf den begin des integers - * @return wert des integers oder 0 bei fehler + * @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_atoi(struct net_pkt *pkt, void *p) { - int val = 0; - void *max = ((void *)pkt->buf + pkt->size); - char *c = p; +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 ((void *)pkt->buf > p || p > max) - return 0; + if (pkt->buf > c || c > max) + return -1; + + *val = 0; /* ziffern einlesen */ - while (isdigit(*c) && (void *)c < max) - val = (val * 10) + (*c++ - 0x30); + while (isdigit(*c) && c < max) + *val = (*val * 10) + (*c++ - 0x30); - return val; + return (c - (pkt->buf + offset)); +} + +/** + * pkt_parse_ip() + * gibt die IP in *.*.*.* ab p als integer zurueck + * + * @param *pkt daten vom gameserver + * @param offset offset auf den begin der IP + * @param *ip pointer auf eine in_addr struct + * @return Anzahl der gelesenen Zeichen oder 0 bei Fehler + */ +int pkt_parse_ip(struct net_pkt *pkt, int offset, struct in_addr *ip) +{ + int i, tmp, count, pos = offset; + ip->s_addr = 0; + + for (i = 0; i < 4; i++) { + count = pkt_parse_int(pkt, pos, &tmp); + pos += count; + if (count == 0 || tmp < 0 || tmp > 255) + return 0; + + ip->s_addr = ip->s_addr>>8 | tmp<<24; + + if (i != 3 && pkt->buf[pos++] != '.') + return 0; + } + return pos - offset; +} + +/** + * pkt_print() + * gibt ein paket als hex-dump aus + * + * @param *pkt daten vom gameserver + * @return char pointer auf hex-dump + */ +char * pkt_print(struct net_pkt *pkt) +{ + int pos = 0, i = 0, j; + char *buf = malloc(pkt->size * 4 + 64); + + while (pos < pkt->size) { + i += sprintf(buf + i, "%04X: ", pos); + for (j = 0; j < 16; j++) { + if (pos + j < pkt->size) + i += sprintf(buf + i, "%02X", pkt->buf[pos + j]); + else + i += sprintf(buf + i, " "); + + if (j % 2) + buf[i++] = ' '; + } + + for (j = 0; j < 16; j++) { + if (pos + j < pkt->size) { + unsigned char val = pkt->buf[pos + j]; + if (val >= 0x20 && val < 0x80) + buf[i++] = val; + else + buf[i++] = '.'; + } else { + buf[i++] = ' '; + } + } + + pos += 16; + buf[i++] = '\r'; + buf[i++] = '\n'; + } + buf[i] = 0; + return buf; } diff --git a/src/scanner.c b/src/scanner.c index 6587bf0..7106315 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -27,22 +27,39 @@ #include #include #include +#include #include #include "list.h" #include "netpkt.h" #include "configfile.h" #include "hlswmaster.h" +#include "plugin.h" +#include "plugin_helper.h" #define DEBUG 1 +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 */ +/** scan socket */ static int scan_sock; /** @@ -53,7 +70,8 @@ static int scan_sock; * @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) { +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); } @@ -66,10 +84,11 @@ static inline int server_cmp(const struct game_server *a, struct game_server *b) * @param gameid gameid des servers * @param ip ip des servers * @param port1 erster port - * @param port2 zweiter port + * @param port2 zweiter port * @return false bei fehler */ -int server_add(u_int16_t gameid, u_int32_t ip, u_int16_t port1, u_int16_t port2) { +int server_add(u_int16_t gameid, u_int32_t ip, u_int16_t port1, u_int16_t port2) +{ struct game_server server, *nserver; server.gameid = gameid; @@ -99,7 +118,7 @@ int server_add(u_int16_t gameid, u_int32_t ip, u_int16_t port1, u_int16_t port2) memcpy(nserver, &server, sizeof(struct game_server)); list_add_tail(&nserver->list, &serverlist); } - + /* modtime anpassen */ nserver->modtime = time(NULL); @@ -114,7 +133,8 @@ int server_add(u_int16_t gameid, u_int32_t ip, u_int16_t port1, u_int16_t port2) * * @param timeout timeout in sekunden */ -static void serverlist_refresh(long timeout) { +static void serverlist_refresh(long timeout) +{ struct game_server *server, *tmp; long now = time(NULL); @@ -151,35 +171,87 @@ static void serverlist_refresh(long timeout) { * @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; int ret = 1; - + addr.sin_family = AF_INET; addr.sin_port = htons(dstport); addr.sin_addr.s_addr = (dstip ? dstip->s_addr : 0xFFFFFFFF); - + if (sendto(scan_sock, buf, size, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { log_print("scan_send(): sendto()"); ret = 0; } - + usleep(10000); return ret; } /** - * scan_control() - * triggert den scan der plugins - * arbeitet dann die sender queue ab - * triggert den gc der plugins + * scan_receive_real() + * arbeitet die receive queue ab */ -void scan_control(void) { - struct net_pkt *pkt, *tmp; - long now; +void scan_receive_real(void) +{ + struct rx_entry *rx; + struct net_pkt *pkt; + int cnt, retval; + + do { + sem_wait(&rxlist_sem); + + pthread_mutex_lock(&rxlist_lock); + rx = list_entry(rxlist.next, struct rx_entry, list); + list_del(&rx->list); + pthread_mutex_unlock(&rxlist_lock); + + pkt = rx->pkt; + free(rx); + + retval = plugins_parse(pkt); + + switch (retval) { + case PARSE_REJECT: + log_print("scan_receive(): unknown packet: %s:%d size:%d", + inet_ntoa(pkt->addr.sin_addr), + ntohs(pkt->addr.sin_port), + pkt->size); +#if 0 + { + char *p = pkt_print(pkt); + log_print("%s", p); + free(p); + } +#endif + + case PARSE_ACCEPT: + free(pkt); + + case PARSE_ACCEPT_FREED: + break; + } + + sem_getvalue(&rxlist_sem, &cnt); + } while (cnt > 0); + +} + +/** + * 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; 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); @@ -190,10 +262,10 @@ void scan_control(void) { log_print(" serverlist_refresh: %ds", list_refresh); log_print(" serverlist_timeout: %ds", list_timeout); log_print(" plugin_timeout: %d", plugin_timeout); - + while (1) { now = time(NULL); - + /* interne plugin daten aufraeumen */ if (last_plugin_gc + plugin_timeout < now) { last_plugin_gc = now; @@ -205,29 +277,36 @@ void scan_control(void) { last_list_refresh = now; serverlist_refresh(list_timeout); } - + /* neuen scan ausloesen */ if (last_scan + scan_interval < now) { last_scan = now; plugins_scan(); } - sleep(1); + /* empfangene daten parsen */ + sem_getvalue(&rxlist_sem, &cnt); + if (cnt > 0) + scan_receive_real(); + + usleep(500000); } } /** * scan_receive() - * wartet auf serverantworten und uebergibt die pakete den - * plugins zur auswertung + * wartet auf serverantworten und uebergibt die pakete der + * receive queue */ -void scan_receive(void) { +void scan_receive(void) +{ struct net_pkt *pkt; + struct rx_entry *rx; fd_set fdsel, fdcpy; int recvsize, i; log_print("thread_start: scan_receiver"); - + FD_ZERO(&fdsel); FD_SET(scan_sock, &fdsel); @@ -235,13 +314,11 @@ void scan_receive(void) { memcpy(&fdcpy, &fdsel, sizeof(fdsel)); select(FD_SETSIZE, &fdcpy, NULL, NULL, NULL); - ioctl(scan_sock, FIONREAD, &recvsize); - - if (recvsize <= 0) { - log_print("scan_receive(): drop short packet"); + 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; @@ -250,29 +327,40 @@ void scan_receive(void) { i = sizeof(struct sockaddr_in); pkt->size = recvfrom(scan_sock, pkt->buf, recvsize, 0, (struct sockaddr *)&pkt->addr, &i); - if (!plugins_parse(pkt)) { - log_print("scan_receive(): unknown packet: %s:%d size:%d", - inet_ntoa(pkt->addr.sin_addr), ntohs(pkt->addr.sin_port), pkt->size); + if (pkt->size == 0) { + free(pkt); + continue; } + + rx = malloc(sizeof(struct rx_entry)); + rx->pkt = pkt; - free(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() { +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() { +int scan_init() +{ struct sockaddr_in dst; int i = 1, port; char *ip; @@ -298,12 +386,15 @@ int scan_init() { log_print("scan_init(): setsockopt()"); return 0; } - + if (atexit(scan_close) != 0) { log_print("scan_init(): atexit()"); return 0; } log_print("scan socket initialized (%s:%d)", ip, port); + + sem_init(&rxlist_sem, 0, 0); + return 1; } diff --git a/tools/masterquery.c b/tools/masterquery.c index 62f2b51..1d86dfd 100644 --- a/tools/masterquery.c +++ b/tools/masterquery.c @@ -68,7 +68,8 @@ static char *id2name[] = { static int sock, verbose = 0; -static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) { +static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) +{ struct _entry *server; struct in_addr tmp; @@ -77,7 +78,7 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) { inet_ntoa(src->sin_addr), ntohs(src->sin_port), size); } else { - printf("received hlsw packet from: %15s:%-5d size=%d\n count=%d", + printf("received hlsw packet from: %15s:%-5d size=%d count=%d\n", inet_ntoa(src->sin_addr), ntohs(src->sin_port), size, ((size > sizeof(hlswheader)) ? (size - sizeof(hlswheader)) / sizeof(struct _entry) : 0)); @@ -94,7 +95,8 @@ static void parse_pkt(struct sockaddr_in *src, void *pkt, unsigned int size) { } } -static int scan_init() { +static int scan_init() +{ int i = 1; if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { @@ -110,7 +112,8 @@ static int scan_init() { return 0; } -static int scan_transmit(struct sockaddr_in *dst) { +static int scan_transmit(struct sockaddr_in *dst) +{ if (sendto(sock, hlswheader, sizeof(hlswheader), 0, (struct sockaddr *)dst, sizeof(struct sockaddr_in)) < 0) { perror("sendto()"); return -1; @@ -119,7 +122,8 @@ static int scan_transmit(struct sockaddr_in *dst) { return 0; } -static int scan_receive() { +static int scan_receive() +{ struct sockaddr_in src; struct timeval tv; fd_set fdsel; @@ -171,7 +175,8 @@ static struct option opts[] = { {0, 0, 0, 0} }; -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ struct sockaddr_in dst; int arg = 0, code = 0; int freq = -1;