/*************************************************************************** * 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 "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..) */ { 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 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; 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, };