Version 0.10
This commit is contained in:
commit
c4d38c9cd0
29
Makefile
Normal file
29
Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
# Toplevel Makefile
|
||||
|
||||
MASTER_SRC := src/client.c src/daemon.c src/logging.c src/main.c src/plugin.c src/plugin_helper.c src/scanner.c src/serverlist.c
|
||||
PLUGIN_SRC := plugins/quake3.c
|
||||
|
||||
CFLAGS := -Wall -I. -I./include -g -fPIC
|
||||
|
||||
DEPFILES := $(PLUGIN_SRC:%.c=%.d) $(MASTER_SRC:%.c=%.d)
|
||||
|
||||
# ############################
|
||||
|
||||
all: $(DEPFILES) $(PLUGIN_SRC:%.c=%.so) src/hlswmaster
|
||||
|
||||
src/hlswmaster: $(MASTER_SRC:%.c=%.o)
|
||||
gcc -ldl -lpthread -rdynamic $^ -o $@
|
||||
|
||||
%.d: %.c
|
||||
@-$(CC) -M -MG $(CFLAGS) $< > $@
|
||||
|
||||
%.o: %.c %.d
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
%.so: %.o
|
||||
$(LD) -shared -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf src/hlswmaster src/*.o plugins/*.so plugins/*.o $(DEPFILES)
|
||||
|
||||
-include $(DEPFILES)
|
48
include/hlswmaster.h
Normal file
48
include/hlswmaster.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef _HLSWMASTER_H
|
||||
#define _HLSWMASTER_H
|
||||
|
||||
#include "list.h"
|
||||
#include "netpkt.h"
|
||||
|
||||
struct game_server {
|
||||
/* must be first */
|
||||
struct list_head list;
|
||||
|
||||
u_int16_t gameid;
|
||||
u_int32_t ip;
|
||||
u_int16_t port1;
|
||||
u_int16_t port2;
|
||||
|
||||
unsigned long modtime;
|
||||
};
|
||||
|
||||
/* daemon.c */
|
||||
void daemonize(char *pw_name);
|
||||
|
||||
/* logging.c */
|
||||
void log_open(char *logfile);
|
||||
void log_close();
|
||||
void log_print(const char *fmt, ... );
|
||||
|
||||
/* plugin.c */
|
||||
int plugin_load(char *name);
|
||||
int plugin_unload(char *name);
|
||||
int plugins_scan(void);
|
||||
int plugins_parse(struct net_pkt *pkt);
|
||||
int plugins_gc(unsigned long timeout);
|
||||
|
||||
/* scanner.c */
|
||||
int scan_init(void);
|
||||
void scan_exit(void);
|
||||
void scan_transmit(void);
|
||||
void scan_receive(void);
|
||||
|
||||
/* serverlist.c */
|
||||
void server_collector(void);
|
||||
|
||||
/* client.c */
|
||||
int client_pkt_add(struct game_server *server);
|
||||
int client_pkt_commit(void);
|
||||
void client_handler(void);
|
||||
|
||||
#endif
|
267
include/list.h
Normal file
267
include/list.h
Normal file
@ -0,0 +1,267 @@
|
||||
#ifndef _LIST_H
|
||||
#define _LIST_H
|
||||
|
||||
/*
|
||||
** stolen from linux kernel (2.6.11)
|
||||
** 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
|
||||
*/
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
#define INIT_LIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
static inline void __list_splice(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
struct list_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
#endif
|
||||
|
||||
/* Return pointer to first true entry, if any, or NULL. A macro
|
||||
required to allow inlining of cmpfn. */
|
||||
#define LIST_FIND(head, cmpfn, type, args...) \
|
||||
({ \
|
||||
const struct list_head *__i, *__j = NULL; \
|
||||
\
|
||||
list_for_each(__i, (head)) \
|
||||
if (cmpfn((const type)__i , ## args)) { \
|
||||
__j = __i; \
|
||||
break; \
|
||||
} \
|
||||
(type)__j; \
|
||||
})
|
20
include/netpkt.h
Normal file
20
include/netpkt.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _NETPKT_H
|
||||
#define _NETPKT_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
struct net_pkt {
|
||||
/* must be first */
|
||||
struct list_head list;
|
||||
|
||||
struct sockaddr_in addr;
|
||||
unsigned int size;
|
||||
unsigned char buf[0];
|
||||
|
||||
};
|
||||
|
||||
#endif
|
31
include/plugin.h
Normal file
31
include/plugin.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _PLUGIN_H
|
||||
#define _PLUGIN_H
|
||||
|
||||
#include "netpkt.h"
|
||||
#include "list.h"
|
||||
|
||||
extern void pkt_queue(struct net_pkt *pkt);
|
||||
extern int server_add(unsigned int gameid, struct in_addr ip, u_int16_t port1, u_int16_t port2);
|
||||
|
||||
extern struct net_pkt * pkt_factory(char *dstip, unsigned int dstport, char *buf, unsigned int size);
|
||||
|
||||
extern int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size);
|
||||
extern int pkt_strcmp(struct net_pkt *pkt, unsigned int offset, char *search);
|
||||
|
||||
struct hlswmaster_plugin {
|
||||
/* must be first */
|
||||
struct list_head list;
|
||||
|
||||
char name[32];
|
||||
|
||||
int (*init)(struct list_head *config);
|
||||
int (*fini)(void);
|
||||
int (*scan)(void);
|
||||
int (*parse)(struct net_pkt *pkt);
|
||||
int (*gc)(int timeout);
|
||||
};
|
||||
|
||||
extern void register_plugin(struct hlswmaster_plugin *me);
|
||||
extern void unregister_plugin(struct hlswmaster_plugin *me);
|
||||
|
||||
#endif
|
49
plugins/quake3.c
Normal file
49
plugins/quake3.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include "plugin.h"
|
||||
|
||||
#define GAMEID_Q3 0x0006
|
||||
|
||||
int scan(void) {
|
||||
struct net_pkt *scan;
|
||||
unsigned int port;
|
||||
char msg[] = "\xff\xff\xff\xffgetstatus";
|
||||
|
||||
for (port = 27960; port <= 27963; port++) {
|
||||
if (!(scan = pkt_factory(NULL, port, msg, 13)))
|
||||
return 0;
|
||||
|
||||
pkt_queue(scan);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int parse(struct net_pkt *pkt) {
|
||||
if (!pkt_strcmp(pkt, 0, "\xff\xff\xff\xffstatusResponse")) {
|
||||
server_add(GAMEID_Q3, pkt->addr.sin_addr, pkt->addr.sin_port, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init(struct list_head *config) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fini(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct hlswmaster_plugin quake3_plugin = {
|
||||
.name = "quake3",
|
||||
.init = &init,
|
||||
.fini = &fini,
|
||||
.scan = &scan,
|
||||
.parse = &parse,
|
||||
};
|
||||
|
||||
void _init(void) {
|
||||
register_plugin(&quake3_plugin);
|
||||
}
|
||||
|
||||
void _fini(void) {
|
||||
unregister_plugin(&quake3_plugin);
|
||||
}
|
197
src/client.c
Normal file
197
src/client.c
Normal file
@ -0,0 +1,197 @@
|
||||
/***************************************************************************
|
||||
* 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 "plugin.h"
|
||||
#include "list.h"
|
||||
|
||||
#define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00"
|
||||
#define HLSW_HEADER_LEN 0x12
|
||||
#define MAX_PKT_LEN (1400 + HLSW_HEADER_LEN)
|
||||
|
||||
struct client_pkt {
|
||||
struct list_head list;
|
||||
unsigned int size;
|
||||
unsigned char buf[0];
|
||||
};
|
||||
|
||||
/* real und prepare list. enthalten die client-antworten */
|
||||
LIST_HEAD(client_pkt_list);
|
||||
LIST_HEAD(client_pkt_prepare);
|
||||
|
||||
/* sichert die real list ab */
|
||||
static pthread_mutex_t pkt_list_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/**
|
||||
* pkt_not_full()
|
||||
* LIST_FIND helper
|
||||
*
|
||||
* @param struct client_pkt *a
|
||||
* @param void *b
|
||||
* @return true wenn das paket noch nicht voll ist
|
||||
*/
|
||||
static inline int pkt_not_full(const struct client_pkt *a, void *b) {
|
||||
return (a->size < MAX_PKT_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* client_pkt_add_real()
|
||||
* erzeugt eine neues client_pkt und fuegt es einer liste hinzu
|
||||
* gibt einen pointer auf das neue client_pkt zurueck
|
||||
*
|
||||
* @param struct list_head *list
|
||||
* @return struct client_pkt * or NULL on error
|
||||
*/
|
||||
static struct client_pkt * client_pkt_add_real(struct list_head *list) {
|
||||
struct client_pkt *new;
|
||||
|
||||
if (!(new = malloc(sizeof(struct client_pkt) + MAX_PKT_LEN)))
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
|
||||
new->size = HLSW_HEADER_LEN;
|
||||
memcpy(new->buf, HLSW_HEADER, HLSW_HEADER_LEN);
|
||||
|
||||
list_add_tail(&new->list, list);
|
||||
return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* client_pkt_add()
|
||||
* fuegt dem freien client_pkt der prepare-liste ein Spiel hinzu
|
||||
* wenn kein platz vorhanden ist, wird ein neues paket erzeugt
|
||||
*
|
||||
* @param struct game_server *server
|
||||
* @return false bei fehler (malloc)
|
||||
*
|
||||
* TODO: memcpy() durch pointer ops ersetzen?
|
||||
*/
|
||||
int client_pkt_add(struct game_server *server) {
|
||||
struct client_pkt *pkt;
|
||||
|
||||
char *src, *dst;
|
||||
|
||||
pkt = LIST_FIND(&client_pkt_prepare, pkt_not_full, struct client_pkt *, NULL);
|
||||
if (!pkt && !(pkt = client_pkt_add_real(&client_pkt_prepare)))
|
||||
return 0;
|
||||
|
||||
dst = (char *)&pkt->buf + pkt->size;
|
||||
|
||||
src = (char *)&server->gameid;
|
||||
memcpy(dst, src, 2);
|
||||
|
||||
src = (char *)&server->ip;
|
||||
memcpy(dst +2, src, 4);
|
||||
|
||||
src = (char *)&server->port1;
|
||||
memcpy(dst +6, src, 4);
|
||||
|
||||
src = (char *)&server->port2;
|
||||
memcpy(dst +8, src, 4);
|
||||
|
||||
pkt->size += 10;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* client_pkt_commit()
|
||||
*
|
||||
* die prepare liste die echte liste und alle pakete der
|
||||
* alten liste werden entfernt
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
int client_pkt_commit() {
|
||||
struct list_head old_list, *pkt, *tmp;
|
||||
|
||||
pthread_mutex_lock(&pkt_list_lock);
|
||||
|
||||
/* old_list wird head der real 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(&pkt_list_lock);
|
||||
|
||||
/* alle alten pakete entfernen */
|
||||
list_for_each_safe(pkt, tmp, &old_list) {
|
||||
list_del(pkt);
|
||||
free(pkt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* client_handler()
|
||||
* empfaengt und beantwortet die HLSW Anfragen
|
||||
*
|
||||
* TODO: src-ip configurierbar machen (config file?)
|
||||
*/
|
||||
void client_handler(void) {
|
||||
struct client_pkt *pkt;
|
||||
struct sockaddr_in dst;
|
||||
int sock, i;
|
||||
unsigned char buf[32];
|
||||
|
||||
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
log_print("client_handler(): socket()");
|
||||
return;
|
||||
}
|
||||
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(7140);
|
||||
inet_aton("0.0.0.0", &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(&pkt_list_lock);
|
||||
|
||||
/* unsere vorbereiteten pakete ausgeben */
|
||||
list_for_each_entry(pkt, &client_pkt_list, list)
|
||||
sendto(sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&dst, sizeof(dst));
|
||||
|
||||
pthread_mutex_unlock(&pkt_list_lock);
|
||||
}
|
||||
}
|
50
src/daemon.c
Normal file
50
src/daemon.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "hlswmaster.h"
|
||||
|
||||
void daemonize(char *pw_name) {
|
||||
int fail = 0;
|
||||
pid_t pid;
|
||||
struct passwd *pwl;
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
log_print("daemonize(): fork()");
|
||||
exit(-1);
|
||||
|
||||
} else if (pid != 0) {
|
||||
exit(0);
|
||||
|
||||
} else {
|
||||
/* Kindprozess weiterlaufen lassen */
|
||||
if (setsid() == -1) {
|
||||
log_print("daemonize: setsid()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
umask(0) ;
|
||||
|
||||
if (pw_name != NULL) {
|
||||
if((pwl = getpwnam(pw_name)) == NULL) {
|
||||
log_print("mkdaemon(): getpwnam(\"%s\")", pw_name);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (setgid(pwl->pw_gid) != 0) {
|
||||
log_print("mkdaemon(): setgid()");
|
||||
fail = 1;
|
||||
}
|
||||
|
||||
if (setuid(pwl->pw_uid) != 0) {
|
||||
log_print("mkdaemon(): setuid()");
|
||||
fail = 1;
|
||||
}
|
||||
|
||||
if (fail == 0) {
|
||||
log_print("user changed to: %s, pid: %d", pw_name, getpid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
src/logging.c
Normal file
53
src/logging.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static FILE *log_fd = NULL;
|
||||
|
||||
void log_close() {
|
||||
fclose(log_fd);
|
||||
}
|
||||
|
||||
void log_open(char *logfile) {
|
||||
if ((log_fd = fopen(logfile, "a" )) == NULL) {
|
||||
fprintf(stderr, "log_open(\"%s\"): %s\n", logfile, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
if (atexit(log_close) != 0) {
|
||||
fprintf(stderr, "log_open(): atexit(): %s\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void log_print(const char *fmt, ...) {
|
||||
va_list az;
|
||||
time_t tzgr;
|
||||
char tbuf[64];
|
||||
char buffer[256];
|
||||
|
||||
va_start(az, fmt);
|
||||
vsprintf(buffer, fmt, az);
|
||||
va_end(az);
|
||||
|
||||
time(&tzgr);
|
||||
strftime(tbuf, 64, "%b %d %H:%M:%S :", localtime(&tzgr));
|
||||
|
||||
if (log_fd) {
|
||||
if (errno) {
|
||||
fprintf(log_fd, "%s %s: %s\n", tbuf, buffer, strerror(errno));
|
||||
} else {
|
||||
fprintf(log_fd, "%s %s\n", tbuf, buffer);
|
||||
}
|
||||
} else {
|
||||
if (errno) {
|
||||
fprintf(stderr, "%s %s: %s\n", tbuf, buffer, strerror(errno));
|
||||
} else {
|
||||
fprintf(stderr, "%s %s\n", tbuf, buffer);
|
||||
}
|
||||
}
|
||||
errno = 0;
|
||||
fflush(log_fd);
|
||||
}
|
77
src/main.c
Normal file
77
src/main.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "hlswmaster.h"
|
||||
|
||||
static struct option opts[] = {
|
||||
{"config", 1, 0, 'c'},
|
||||
{"user", 1, 0, 'u'},
|
||||
{"debug", 1, 0, 'd'},
|
||||
{"no-daemon", 0, 0, 'f'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int arg = 0, code = 0;
|
||||
pthread_t thread1, thread2, thread3, thread4;
|
||||
|
||||
while (code != -1) {
|
||||
code = getopt_long(argc, argv, "c:u:d:fh", opts, &arg);
|
||||
|
||||
switch (code) {
|
||||
case 'c': /* config */
|
||||
break;
|
||||
|
||||
case 'u': /* user */
|
||||
break;
|
||||
|
||||
case 'd': /* debug */
|
||||
break;
|
||||
|
||||
case 'f': /* no-daemon */
|
||||
break;
|
||||
|
||||
case 'h': /* help */
|
||||
printf("Usage: hlsw-master [options]\n"
|
||||
"Options: \n"
|
||||
" --config -c configfile use this configfile\n"
|
||||
" --user -u username change uid to username\n"
|
||||
" --debug -d debuglevel level: 0 - 7\n"
|
||||
" --no-daemon -f do not fork\n"
|
||||
" --help -h this help\n"
|
||||
"\n");
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case '?': /* error */
|
||||
exit(-1);
|
||||
break;
|
||||
|
||||
default: /* unknown / all options parsed */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plugin_load("plugins/quake3.so");
|
||||
|
||||
scan_init();
|
||||
|
||||
pthread_create(&thread1, NULL, (void *)&scan_transmit, NULL);
|
||||
pthread_create(&thread2, NULL, (void *)&scan_receive, NULL);
|
||||
pthread_create(&thread3, NULL, (void *)&server_collector, NULL);
|
||||
pthread_create(&thread4, NULL, (void *)&client_handler, NULL);
|
||||
|
||||
|
||||
sleep(9999);
|
||||
|
||||
scan_exit();
|
||||
|
||||
return 0;
|
||||
}
|
104
src/plugin.c
Normal file
104
src/plugin.c
Normal file
@ -0,0 +1,104 @@
|
||||
/***************************************************************************
|
||||
* 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 <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "hlswmaster.h"
|
||||
#include "plugin.h"
|
||||
#include "netpkt.h"
|
||||
#include "list.h"
|
||||
|
||||
/* liste der geladenen plugins */
|
||||
LIST_HEAD(plugin_list);
|
||||
|
||||
/* sichert die plugin list UND die plugins ab */
|
||||
static pthread_mutex_t plugin_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
int plugin_load(char *name) {
|
||||
void *tmp;
|
||||
|
||||
if (!(tmp = dlopen(name, RTLD_NOW)))
|
||||
log_print("unable to load plugin '%s'", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_unload(char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugins_scan(void) {
|
||||
struct hlswmaster_plugin *plugin;
|
||||
|
||||
pthread_mutex_lock(&plugin_lock);
|
||||
list_for_each_entry(plugin, &plugin_list, list)
|
||||
if (plugin->scan && !plugin->scan())
|
||||
log_print("plugin %s: scan error", plugin->name);
|
||||
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int plugins_parse(struct net_pkt *pkt) {
|
||||
struct hlswmaster_plugin *plugin;
|
||||
|
||||
pthread_mutex_lock(&plugin_lock);
|
||||
list_for_each_entry(plugin, &plugin_list, list) {
|
||||
if (plugin->parse && plugin->parse(pkt)) {
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugins_gc(unsigned long timeout) {
|
||||
struct hlswmaster_plugin *plugin;
|
||||
|
||||
pthread_mutex_lock(&plugin_lock);
|
||||
list_for_each_entry(plugin, &plugin_list, list)
|
||||
if (plugin->gc)
|
||||
plugin->gc(timeout);
|
||||
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* called vom plugin:_init() after local init */
|
||||
void register_plugin(struct hlswmaster_plugin *me) {
|
||||
if (!me->init || (me->init(NULL))) {
|
||||
pthread_mutex_lock(&plugin_lock);
|
||||
list_add((struct list_head *)me, &plugin_list);
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* called vom plugin:_fini() after local finish */
|
||||
void unregister_plugin(struct hlswmaster_plugin *me) {
|
||||
pthread_mutex_lock(&plugin_lock);
|
||||
list_del((struct list_head *)me);
|
||||
pthread_mutex_unlock(&plugin_lock);
|
||||
|
||||
if (me->fini)
|
||||
me->fini();
|
||||
}
|
48
src/plugin_helper.c
Normal file
48
src/plugin_helper.c
Normal file
@ -0,0 +1,48 @@
|
||||
#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 "plugin.h"
|
||||
#include "netpkt.h"
|
||||
|
||||
struct net_pkt * pkt_factory(char *dstip, unsigned int dstport, char *buf, unsigned int size) {
|
||||
struct net_pkt *pkt;
|
||||
|
||||
if (!(pkt = malloc(sizeof(struct net_pkt) + size)))
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&pkt->list);
|
||||
|
||||
pkt->addr.sin_family = AF_INET;
|
||||
pkt->addr.sin_port = htons(dstport);
|
||||
|
||||
if (dstip)
|
||||
inet_aton(dstip, &pkt->addr.sin_addr);
|
||||
else
|
||||
inet_aton("255.255.255.255", &pkt->addr.sin_addr);
|
||||
|
||||
pkt->size = size;
|
||||
memcpy(pkt->buf, buf, size);
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
int pkt_memcmp(struct net_pkt *pkt, unsigned int offset, char *search, unsigned int size) {
|
||||
|
||||
if (offset >= pkt->size)
|
||||
return 1;
|
||||
|
||||
if (offset + size >= pkt->size)
|
||||
size = pkt->size - offset;
|
||||
|
||||
return memcmp(pkt->buf, search, size);
|
||||
}
|
||||
|
||||
int pkt_strcmp(struct net_pkt *pkt, unsigned int offset, char *search) {
|
||||
return pkt_memcmp(pkt, offset, search, strlen(search));
|
||||
}
|
119
src/scanner.c
Normal file
119
src/scanner.c
Normal file
@ -0,0 +1,119 @@
|
||||
/***************************************************************************
|
||||
* 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 <sys/ioctl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "netpkt.h"
|
||||
#include "hlswmaster.h"
|
||||
|
||||
LIST_HEAD(send_queue);
|
||||
|
||||
static int scan_sock;
|
||||
|
||||
void pkt_queue(struct net_pkt *pkt) {
|
||||
list_add_tail(&pkt->list, &send_queue);
|
||||
}
|
||||
|
||||
void scan_transmit(void) {
|
||||
struct net_pkt *pkt, *tmp;
|
||||
|
||||
while (1) {
|
||||
plugins_scan();
|
||||
|
||||
list_for_each_entry_safe(pkt, tmp, &send_queue, list) {
|
||||
if (sendto(scan_sock, pkt->buf, pkt->size, 0, (struct sockaddr *)&pkt->addr, sizeof(pkt->addr)) < 0)
|
||||
log_print("scan_transmit(): sendto()");
|
||||
|
||||
list_del(&pkt->list);
|
||||
free(pkt);
|
||||
}
|
||||
|
||||
sleep(30);
|
||||
}
|
||||
}
|
||||
|
||||
void scan_receive(void) {
|
||||
struct net_pkt *pkt;
|
||||
fd_set fdsel, fdcpy;
|
||||
int recvsize, i;
|
||||
|
||||
FD_ZERO(&fdsel);
|
||||
FD_SET(scan_sock, &fdsel);
|
||||
|
||||
while (1) {
|
||||
memcpy(&fdcpy, &fdsel, sizeof(fdsel));
|
||||
select(FD_SETSIZE, &fdcpy, NULL, NULL, NULL);
|
||||
|
||||
ioctl(scan_sock, FIONREAD, &recvsize);
|
||||
|
||||
if (recvsize <= 0)
|
||||
continue;
|
||||
|
||||
if (!(pkt = malloc(sizeof(struct net_pkt) + recvsize)))
|
||||
continue;
|
||||
|
||||
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("received unknown packet\n");
|
||||
|
||||
free(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
int scan_init() {
|
||||
struct sockaddr_in dst;
|
||||
int i;
|
||||
|
||||
if ((scan_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
log_print("scan_init(): socket()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(7130);
|
||||
inet_aton("0.0.0.0", &dst.sin_addr);
|
||||
|
||||
if (bind(scan_sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) {
|
||||
log_print("scan_init(): bind()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setsockopt(scan_sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))) {
|
||||
log_print("scan_init(): setsockopt()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void scan_exit() {
|
||||
close(scan_sock);
|
||||
}
|
122
src/serverlist.c
Normal file
122
src/serverlist.c
Normal file
@ -0,0 +1,122 @@
|
||||
/***************************************************************************
|
||||
* 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 <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "hlswmaster.h"
|
||||
#include "plugin.h"
|
||||
#include "list.h"
|
||||
|
||||
LIST_HEAD(server_list);
|
||||
|
||||
/* sichert die server liste */
|
||||
static pthread_mutex_t server_list_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/**
|
||||
* server_cmp()
|
||||
* LIST_FIND helper
|
||||
*
|
||||
* @param struct game_server *a
|
||||
* @param struct game_server *b
|
||||
* @return true wenn es sich um den selben server handelt
|
||||
*/
|
||||
static inline int server_cmp(const struct game_server *a, struct game_server *b) {
|
||||
return (a->gameid == b->gameid && a->ip == b->ip &&
|
||||
a->port1 == b->port1 && a->port2 == b->port2);
|
||||
}
|
||||
|
||||
/**
|
||||
* server_add()
|
||||
* fuegt der internen serverliste einen server hinzu
|
||||
* wenn dieser server schon in der liste vorhanden ist, wird nur
|
||||
* die modtime angepasst
|
||||
*
|
||||
* @param unsigned int gameid
|
||||
* @param struct in_addr ip
|
||||
* @param u_int16_t port1
|
||||
* @param u_int16_t port2
|
||||
* @return false bei fehler
|
||||
*/
|
||||
int server_add(unsigned int gameid, struct in_addr ip, u_int16_t port1, u_int16_t port2) {
|
||||
struct game_server server, *nserver;
|
||||
|
||||
server.gameid = gameid;
|
||||
server.ip = ip.s_addr;
|
||||
server.port1 = ntohs(port1);
|
||||
server.port2 = ntohs(port2);
|
||||
|
||||
pthread_mutex_lock(&server_list_lock);
|
||||
|
||||
/* diesen server in der liste suchen */
|
||||
nserver = LIST_FIND(&server_list, server_cmp, struct game_server *, &server);
|
||||
if (!nserver) {
|
||||
/* neuen eintrag anlegen */
|
||||
if (!(nserver = malloc(sizeof(struct game_server)))) {
|
||||
pthread_mutex_unlock(&server_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(nserver, &server, sizeof(struct game_server));
|
||||
list_add_tail(&nserver->list, &server_list);
|
||||
}
|
||||
/* modtime anpassen */
|
||||
nserver->modtime = time(NULL);
|
||||
|
||||
pthread_mutex_unlock(&server_list_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* server_collector()
|
||||
* loescht alte server aus der liste
|
||||
* baut aus den verbleibenden die client_liste auf
|
||||
*
|
||||
* TODO: timeout und intervall als config parameter (config file?)
|
||||
*/
|
||||
void server_collector(void) {
|
||||
struct game_server *server, *tmp;
|
||||
unsigned long now, timeout = 120;
|
||||
|
||||
while (1) {
|
||||
sleep(5);
|
||||
|
||||
now = time(NULL);
|
||||
pthread_mutex_lock(&server_list_lock);
|
||||
list_for_each_entry_safe(server, tmp, &server_list, list) {
|
||||
if (server->modtime + timeout < now) {
|
||||
list_del(&server->list);
|
||||
free(server);
|
||||
continue;
|
||||
}
|
||||
client_pkt_add(server);
|
||||
}
|
||||
pthread_mutex_unlock(&server_list_lock);
|
||||
client_pkt_commit();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user