hlswmaster/p_gamespy1.c

302 lines
8.3 KiB
C

/***************************************************************************
* 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 <string.h>
#include <time.h>
#include "logging.h"
#include "event.h"
#include "netpkt.h"
#include "plugin.h"
#include "plugin_helper.h"
static struct scan_ports port_arr[] = {
{ 7777, 7788, 5 }, /* ut(5), ut2k3(14), rune(18), ut2k4(33), aao(15) */
{ 22000, 22010, 16 }, /* bf1942(16) */
{ 23000, 23010, 35 }, /* bfv(35) */
{ 26001, 26011, 19 }, /* igi2(19) */
{ 27888, 27888, 17 }, /* avp2(17) (nur der standart-port..) */
{ 44400, 44400, 51 },
{ 0, 0, 0 }
};
struct gs1_part {
struct list_head list;
unsigned long timeout;
unsigned int queryid;
unsigned int subid;
struct net_pkt *pkt;
};
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_bfv1[] = "bfvietnam\\";
static char reply_bfv2[] = "BFVIETNAM\\";
static char reply_poe[] = "poe\\";
static char reply_opk[] = "opk\\";
static char reply_avp2[] = "avp2\\";
static char reply_igi2[] = "projectigi2r\\";
static char reply_aao[] = "armygame\\";
static char reply_rune[] = "rune\\";
static char reply_postal2[] ="postal2\\";
static int scan(void)
{
pkt_send_portarr(NULL, port_arr, scanmsg, strlen(scanmsg));
return 1;
}
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;
// 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(LOG_INFO, "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 */
if (!pkt_memcmp(pkt, pos1, reply_ut, strlen(reply_ut)))
gameid = 5;
/* unreal tournament 2k3 */
else if (!pkt_memcmp(pkt, pos1, reply_ut2k3, strlen(reply_ut2k4)))
gameid = 14;
/* unreal tournament 2k4 */
else if (!pkt_memcmp(pkt, pos1, reply_ut2k4, strlen(reply_ut2k4)))
gameid = 33;
/* americas army operations */
else if (!pkt_memcmp(pkt, pos1, reply_aao, strlen(reply_aao)))
gameid = 15;
/* postal2 */
else if (!pkt_memcmp(pkt, pos1, reply_postal2, strlen(reply_postal2)))
gameid = 9;
else
return PARSE_REJECT;
break;
case 16:/* battlefield 1942 */
case 35:/* battlefield vietnam */
if (!pkt_memcmp(pkt, pos1, reply_bf1942, strlen(reply_bf1942)))
gameid = 16;
else if (!pkt_memcmp(pkt, pos2, reply_bfv1, strlen(reply_bfv1)))
gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_bfv2, strlen(reply_bfv2)))
gameid = 35;
else if (!pkt_memcmp(pkt, pos2, reply_poe, strlen(reply_poe)))
gameid = 35;
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_memcmp(pkt, pos1, reply_avp2, strlen(reply_avp2)))
gameid = 17;
else
return PARSE_REJECT;
break;
case 18:/* rune */
if (!pkt_memcmp(pkt, pos1, reply_rune, strlen(reply_rune)))
gameid = 18;
else
return PARSE_REJECT;
break;
case 19:/* project igi2 covert strike */
if (!pkt_memcmp(pkt, pos1, reply_igi2, strlen(reply_igi2)))
gameid = 19;
else
return PARSE_REJECT;
break;
default:
return PARSE_REJECT;
}
/* hostport angabe suchen */
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 ((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;
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(void *privdata) {
unsigned int timeout = (int)privdata;
unsigned long now = time(NULL);
struct gs1_part *part, *tmp;
list_for_each_entry_safe(part, tmp, &gs1_partlist, list) {
if (part->timeout + timeout < now) {
log_print(LOG_INFO, "gs1: removing dead fragment");
list_del(&part->list);
free(part->pkt);
free(part);
}
}
return 0;
}
static int init(void)
{
int timeout = config_get_int("gamespy1", "gc_timeout", 90);
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
event_add_timeout(&tv, gc, (void *)timeout);
return 0;
}
struct hlswmaster_plugin plugin = {
.name = "gamespy1",
.scan = &scan,
.parse = &parse,
.init = &init,
};