/*************************************************************************** * Copyright (C) 03/2005 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "hlswmaster.h" #include "configfile.h" #include "list.h" #define HLSW_HEADER "\xFF\xFF\xFF\xFFHLSWLANSEARCH\x00" #define HLSW_HEADER_LEN 0x12 #define HLSW_ENTRY_LEN 10 #define MAX_PKT_LEN (HLSW_HEADER_LEN + HLSW_ENTRY_LEN * 140) struct client_pkt { struct list_head list; unsigned int size; unsigned char buf[0]; }; /** working list (wird zu den clients gesendet) */ static LIST_HEAD(client_pkt_list); /** prepare list (wird im hintergrund zusammengebaut) */ static LIST_HEAD(client_pkt_prepare); /** sichert die work list ab */ static pthread_mutex_t client_pkt_list_lock = PTHREAD_MUTEX_INITIALIZER; /** * client_pkt_not_full() * prueft ob ein client-paket komplett belegt ist * * @param *cpkt pointer auf paket das getestet wird * @param *unused * @return true wenn das paket noch nicht voll ist */ static inline int client_pkt_not_full(const struct client_pkt *cpkt, void *unused) { return (cpkt->size <= (MAX_PKT_LEN - HLSW_ENTRY_LEN)); } /** * client_pkt_add_real() * erzeugt eine neues client_pkt und fuegt es einer liste hinzu * * @param *list liste die das neue paket aufnimmt * @return pointer auf ein neues paket oder NULL bei fehler */ static struct client_pkt * client_pkt_add_real(struct list_head *list) { struct client_pkt *cpkt; cpkt = malloc(sizeof(struct client_pkt) + MAX_PKT_LEN); if (cpkt) { INIT_LIST_HEAD(&cpkt->list); /* kopier den hlsw header in das client paket */ memcpy(cpkt->buf, HLSW_HEADER, HLSW_HEADER_LEN); cpkt->size = HLSW_HEADER_LEN; /* am anfang eingliedern! (suche nach freien paket schneller) */ list_add(&cpkt->list, list); } return cpkt; } /** * client_pkt_add() * fuegt der client_pkt liste einen server hinzu, wenn kein platz * im aktuellen paket vorhanden ist, wird ein neues paket allokiert * * @param *server pointer den server * @return false bei fehler */ int client_pkt_add(struct game_server *server) { struct client_pkt *cpkt; /* sucht ein freies paket, oder erzeugt eins */ cpkt = LIST_FIND(&client_pkt_prepare, client_pkt_not_full, struct client_pkt *, NULL); if (!cpkt && !(cpkt = client_pkt_add_real(&client_pkt_prepare))) return 0; /* kopiert den server eintrag in das client paket */ memcpy((void *)&cpkt->buf + cpkt->size, &server->gameid, HLSW_ENTRY_LEN); cpkt->size += HLSW_ENTRY_LEN; return 1; } /** * client_pkt_commit() * die prepare liste und die work liste werden ausgetauscht * alle pakete der alten work liste werden entfernt * * @return true */ int client_pkt_commit() { struct list_head old_list, *cpkt, *tmp; /* wenn die liste leer ist, nur HLSW-header verschicken */ if (list_empty(&client_pkt_prepare)) client_pkt_add_real(&client_pkt_prepare); pthread_mutex_lock(&client_pkt_list_lock); /* old_list wird head der work list */ list_add(&old_list, &client_pkt_list); list_del(&client_pkt_list); /* client_pkt_list wird head der prepare list */ list_add(&client_pkt_list, &client_pkt_prepare); list_del_init(&client_pkt_prepare); pthread_mutex_unlock(&client_pkt_list_lock); /* alle alten pakete entfernen */ list_for_each_safe(cpkt, tmp, &old_list) { list_del(cpkt); free(cpkt); } return 1; } /** * client_handler() * empfaengt und beantwortet die HLSW anfragen der clients */ void client_handler(void) { struct client_pkt *cpkt; struct sockaddr_in dst; int sock, i; unsigned char buf[32], *ip; if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { log_print("client_handler(): socket()"); return; } ip = config_get_string("global", "master_ip", "0.0.0.0"); log_print("thread_start: client_handler (%s:7140)", ip); dst.sin_family = AF_INET; dst.sin_port = htons(7140); inet_aton(ip, &dst.sin_addr); if (bind(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { log_print("client_handler(): bind()"); return; } while (1) { /* auf clientanfrage warten */ i = sizeof(struct sockaddr_in); recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&dst, &i); /* testen ob es sich um ein HLSW anforderung handelt */ if (memcmp(buf, HLSW_HEADER, HLSW_HEADER_LEN)) continue; pthread_mutex_lock(&client_pkt_list_lock); /* pakete aus der work list senden */ list_for_each_entry(cpkt, &client_pkt_list, list) sendto(sock, cpkt->buf, cpkt->size, 0, (struct sockaddr *)&dst, sizeof(dst)); pthread_mutex_unlock(&client_pkt_list_lock); } }