--- iptables-1.4.0.orig/extensions/libipt_ipp2p.c	2010-11-15 10:27:19.000000000 +0800
+++ iptables-1.4.0/extensions/libipt_ipp2p.c	2010-11-25 11:35:37.000000000 +0800
@@ -1,398 +1,657 @@
-
+/*
+ *	"appe" match extension for iptables
+ *	Eicke Friedrich/Klaus Degner <ipp2p@ipp2p.org>, 2005 - 2006
+ *	Jan Engelhardt <jengelh [at] medozas de>, 2008 - 2009
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License; either
+ *	version 2 of the License, or any later version, as published by the
+ *	Free Software Foundation.
+ */
+#include <stdbool.h>
 #include <stdio.h>
 #include <netdb.h>
 #include <string.h>
 #include <stdlib.h>
 #include <getopt.h>
 #include <ctype.h>
+#include "../include/linux/netfilter/xt_appe.h"
+#include <xtables.h>
 
-#include <iptables.h>
-
-#include <linux/netfilter_ipv4/ipt_ipp2p.h>
-
-static void
-help(void)
+static void ipp2p_mt_help(void)
 {
-    printf(
-    "IPP2P v%s options:\n"
-    " --ipp2p	Grab all known p2p packets\n"
-    " --edk		[TCP&UDP]	All known eDonkey/eMule/Overnet packets\n"
-    " --dc		[TCP] 		All known Direct Connect packets\n"
-    " --kazaa	[TCP&UDP] 	All known KaZaA packets\n"
-    " --gnu		[TCP&UDP]	All known Gnutella packets\n"
-    " --bit		[TCP&UDP]	All known BitTorrent packets\n"
-    " --apple	[TCP] 		All known AppleJuice packets\n"
-    " --winmx	[TCP] 		All known WinMX\n"
-    " --soul		[TCP] 		All known SoulSeek\n"
-    " --ares		[TCP] 		All known Ares\n\n"
-    " EXPERIMENTAL protocols (please send feedback to: ipp2p@ipp2p.org) :\n"
-    " --mute		[TCP]		All known Mute packets\n"
-    " --waste	[TCP]		All known Waste packets\n"
-    " --xdcc		[TCP]		All known XDCC packets (only xdcc login)\n\n"
-    " DEBUG SUPPPORT, use only if you know why\n"
-    " --debug		Generate kernel debug output, THIS WILL SLOW DOWN THE FILTER\n"
-    "\nNote that the follwing options will have the same meaning:\n"
-    " '--ipp2p' is equal to '--edk --dc --kazaa --gnu --bit --apple --winmx --soul --ares'\n"
-    "\nIPP2P was intended for TCP only. Due to increasing usage of UDP we needed to change this.\n"
-    "You can now use -p udp to search UDP packets only or without -p switch to search UDP and TCP packets.\n"
-    "\nSee README included with this package for more details or visit http://www.ipp2p.org\n"
-    "\nExamples:\n"
-    " iptables -A FORWARD -m ipp2p --ipp2p -j MARK --set-mark 0x01\n"
-    " iptables -A FORWARD -p udp -m ipp2p --kazaa --bit -j DROP\n"
-    " iptables -A FORWARD -p tcp -m ipp2p --edk --soul -j DROP\n\n"
-    , IPP2P_VERSION);
+	printf(
+	"ipp2p v%s match options:\n"
+	"  --ruleidx\n"
+	"  --msn\n"
+	"  --msnmsg\n"
+	"  --msnft\n"
+	"  --msngame\n"
+	"  --msnconf\n"
+	"  --msnother\n"
+	"  --ym\n"
+	"  --ymmsg\n"
+	"  --ymfile\n"
+	"  --ymgame\n"
+	"  --ymconf\n"
+	"  --ymother\n"
+	"  --aim\n"
+	"  --aimmsg\n"
+	"  --aimfile\n"
+	"  --aimgame\n"
+	"  --aimconf\n"
+	"  --aimother\n"
+	"  --icq\n"
+	"  --icqmsg\n"
+	"  --icqfile\n"
+	"  --icqgame\n"
+	"  --icqconf\n"
+	"  --icqother\n"
+	"  --wim\n"
+	"  --aim67\n"
+	"  --qq\n"
+	"  --ichat\n"
+	"  --gt\n"
+	"  --gc\n"
+	"  --xfire\n"
+	"  --gadugadu\n"
+	"  --paltalk\n"
+	"  --qnext\n"
+	"  --pocomsg\n"
+	"  --areschat\n"
+	"  --aliww\n"
+	"  --kc\n"
+	"  --lavalava\n"
+	"  --icu2\n"
+	"  --ispq\n"
+	"  --uc\n"
+	"  --mobilemsn\n"
+	"  --baiduhi\n"
+	"  --fetion\n"
+	"  --skype\n"
+	"  --kubao\n"
+	"  --gizmo\n"
+	"  --siprtp\n"
+	"  --teltel\n"
+	"  --teamspeak\n"
+	"  --ss\n"
+	"  --edk\n"
+	"  --ft\n"
+	"  --openft\n"
+	"  --gnu\n"
+	"  --opennap\n"
+	"  --bt\n"
+	"  --xunlei\n"
+	"  --vagaa\n"
+	"  --pp365\n"
+	"  --poco\n"
+	"  --clubbox\n"
+	"  --ares\n"
+	"  --ezpeer\n"
+	"  --pando\n"
+	"  --huntmine\n"
+	"  --kuwo\n"
+	"  --dns\n"
+	"  --ftp\n"
+	"  --http\n"
+	"  --imap\n"
+	"  --irc\n"
+	"  --nntp\n"
+	"  --pop3\n"
+	"  --smb\n"
+	"  --smtp\n"
+	"  --snmp\n"
+	"  --ssh\n"
+	"  --ssltls\n"
+	"  --telnet\n"
+	"  --mssql\n"
+	"  --mysql\n"
+	"  --oracle\n"
+	"  --postgresql\n"
+	"  --sybase\n"
+	"  --db2\n"
+	"  --informix\n"
+	"  --socks\n"
+	"  --pgpnet\n"
+	"  --httpproxy\n"
+	"  --tor\n"
+	"  --vnn\n"
+	"  --softether\n"
+	"  --msteredo\n"
+	"  --ultrasurf\n"
+	"  --hamachi\n"
+	"  --httptunnel\n"
+	"  --pingtunnel\n"
+	"  --tinyvpn\n"
+	"  --realtunnel\n"
+	"  --dynapass\n"
+	"  --ultravpn\n"
+	"  --freeu\n"
+	"  --skyfire\n"
+	"  --mms\n"
+	"  --rtsp\n"
+	"  --tvants\n"
+	"  --ppstream\n"
+	"  --pptv\n"
+	"  --uusee\n"
+	"  --nsplayer\n"
+	"  --pcast\n"
+	"  --tvkoo\n"
+	"  --sopcast\n"
+	"  --udlivex\n"
+	"  --tvuplayer\n"
+	"  --mysee\n"
+	"  --joost\n"
+	"  --flashvideo\n"
+	"  --silverlight\n"
+	"  --slingbox\n"
+	"  --qvod\n"
+	"  --qqlive\n"
+	"  --china\n"
+	"  --shanghai\n"
+	"  --lianzhong\n"
+	"  --bianfeng\n"
+	"  --vnc\n"
+	"  --radmin\n"
+	"  --tvants\n"
+	"  --spyanywhere\n"
+	"  --showmypc\n"
+	"  --logmein\n"
+	"  --teamviewer\n"
+	"  --gogrok\n"
+	"  --remotecontrolpro\n"
+	"  --crossloop\n"
+	"  --windowsrdp\n"
+	"  --pcanywhere\n"
+	"  --windowslivesync\n"
+	"  --sharedview\n"
+	"  --httpupload\n"
+	"  --hinetsafebox\n"
+	"  --msskydrive\n"
+	"  --gdocuploader\n"
+	"  --adrive\n"
+	"  --myotherdrive\n"
+	"  --mozy\n"
+	"  --boxnet\n"
+	"  --officelive\n"
+	"  --readdlestorage\n"
+	"  --dropbox\n"
+	, APPE_VERSION);
 }
 
-static struct option opts[] = {
-        { "ipp2p", 0, 0, '1' },
-        { "edk", 0, 0, '2' },	
-	{ "dc", 0, 0, '7' },
-	{ "gnu", 0, 0, '9' },
-	{ "kazaa", 0, 0, 'a' },
-	{ "bit", 0, 0, 'b' },
-	{ "apple", 0, 0, 'c' },	
-	{ "soul", 0, 0, 'd' },	
-	{ "winmx", 0, 0, 'e' },	
-	{ "ares", 0, 0, 'f' },
-	{ "mute", 0, 0, 'g' },
-	{ "waste", 0, 0, 'h' },
-	{ "xdcc", 0, 0, 'i' },
-	{ "debug", 0, 0, 'j' },
-        {0}
+static const struct option ipp2p_mt_opts[] = {
+	{.name = "ruleidx",				.has_arg = true,	.val = CMD_RULE_INDEX},
+	{.name = "msn",					.has_arg = false,	.val = IM_MSN},
+	{.name = "msnmsg",				.has_arg = false,	.val = IM_MSN_MSG},
+	{.name = "msnfile",				.has_arg = false,	.val = IM_MSN_FILE},
+	{.name = "msngame",				.has_arg = false,	.val = IM_MSN_GAME},
+	{.name = "msnconf",				.has_arg = false,	.val = IM_MSN_CONF},
+	{.name = "msnother",			.has_arg = false,	.val = IM_MSN_OTHER},
+	{.name = "ym",					.has_arg = false,	.val = IM_YM},
+	{.name = "ymmsg",				.has_arg = false,	.val = IM_YM_MSG},
+	{.name = "ymfile",				.has_arg = false,	.val = IM_YM_FILE},
+	{.name = "ymgame",				.has_arg = false,	.val = IM_YM_GAME},
+	{.name = "ymconf",				.has_arg = false,	.val = IM_YM_CONF},
+	{.name = "ymother",				.has_arg = false,	.val = IM_YM_OTHER},
+	{.name = "icq",					.has_arg = false,	.val = IM_ICQ},
+	{.name = "icqmsg",				.has_arg = false,	.val = IM_ICQ_MSG},
+	{.name = "icqfile",				.has_arg = false,	.val = IM_ICQ_FILE},
+	{.name = "icqgame",				.has_arg = false,	.val = IM_ICQ_GAME},
+	{.name = "icqconf",				.has_arg = false,	.val = IM_ICQ_CONF},
+	{.name = "icqother",			.has_arg = false,	.val = IM_ICQ_OTHER},
+	{.name = "aim",					.has_arg = false,	.val = IM_AIM},
+	{.name = "aimmsg",				.has_arg = false,	.val = IM_AIM_MSG},
+	{.name = "aimfile",				.has_arg = false,	.val = IM_AIM_FILE},
+	{.name = "aimgame",				.has_arg = false,	.val = IM_AIM_GAME},
+	{.name = "aimconf",				.has_arg = false,	.val = IM_AIM_CONF},
+	{.name = "aimother",			.has_arg = false,	.val = IM_AIM_OTHER},
+	{.name = "wim",					.has_arg = false,	.val = IM_WEB},
+	{.name = "aim67",				.has_arg = false,	.val = IM_AIM67},
+	{.name = "qq",					.has_arg = false,	.val = IM_QQTM},
+	{.name = "ichat",				.has_arg = false,	.val = IM_ICHAT},
+	{.name = "gt",					.has_arg = false,	.val = IM_JABBER},
+	{.name = "gc",					.has_arg = false,	.val = IM_GOOGLECHAT},
+	{.name = "xfire",				.has_arg = false,	.val = IM_XFIRE},
+	{.name = "gadugadu",			.has_arg = false,	.val = IM_GADUGADU},
+	{.name = "paltalk",				.has_arg = false,	.val = IM_PALTALK},
+	{.name = "qnext",				.has_arg = false,	.val = IM_QNEXT},
+	{.name = "pocomsg",				.has_arg = false,	.val = IM_POCO},
+	{.name = "areachat",			.has_arg = false,	.val = IM_ARES},
+	{.name = "aliww",				.has_arg = false,	.val = IM_ALIWW},
+	{.name = "kc",					.has_arg = false,	.val = IM_KC},
+	{.name = "lavalava",			.has_arg = false,	.val = IM_LAVALAVA},
+	{.name = "icu2",				.has_arg = false,	.val = IM_ICU2},
+	{.name = "ispq",				.has_arg = false,	.val = IM_ISPQ},
+	{.name = "uc",					.has_arg = false,	.val = IM_UC},
+	{.name = "mobilemsn",			.has_arg = false,	.val = IM_MOBILEMSN},
+	{.name = "baiduhi",				.has_arg = false,	.val = IM_BAIDUHI},
+	{.name = "fetion",				.has_arg = false,	.val = IM_FETION},
+	{.name = "skype",				.has_arg = false,	.val = VOIP_SKYPE},
+	{.name = "kubao",				.has_arg = false,	.val = VOIP_KUBAO},
+	{.name = "gizmo",				.has_arg = false,	.val = VOIP_GIZMO},
+	{.name = "siprtp",				.has_arg = false,	.val = VOIP_SIPRTP},
+	{.name = "teltel",				.has_arg = false,	.val = VOIP_TELTEL},
+	{.name = "teamspeak",			.has_arg = false,	.val = VOIP_TEAMSPEAK},
+	{.name = "ss",					.has_arg = false,	.val = P2P_SOULSEEK},
+	{.name = "edk",					.has_arg = false,	.val = P2P_EDONKEY},
+	{.name = "ft",					.has_arg = false,	.val = P2P_FASTTRACK},
+	{.name = "openft",				.has_arg = false,	.val = P2P_OPENFT},
+	{.name = "gnu",					.has_arg = false,	.val = P2P_GNUTELLA},
+	{.name = "opennap",				.has_arg = false,	.val = P2P_OPENNAP},
+	{.name = "bt",					.has_arg = false,	.val = P2P_BITTORRENT},
+	{.name = "xunlei",				.has_arg = false,	.val = P2P_XUNLEI},
+	{.name = "vagaa",				.has_arg = false,	.val = P2P_VAGAA},
+	{.name = "pp365",				.has_arg = false,	.val = P2P_PP365},
+	{.name = "poco",				.has_arg = false,	.val = P2P_POCO},
+	{.name = "clubbox",				.has_arg = false,	.val = P2P_CLUBBOX},
+	{.name = "ares",				.has_arg = false,	.val = P2P_ARES},
+	{.name = "ezpeer",				.has_arg = false,	.val = P2P_EZPEER},
+	{.name = "pando",				.has_arg = false,	.val = P2P_PANDO},
+	{.name = "huntmine",			.has_arg = false,	.val = P2P_HUNTMINE},
+	{.name = "kuwo",				.has_arg = false,	.val = P2P_KUWO},
+	{.name = "dns",					.has_arg = false,	.val = PROT_DNS},
+	{.name = "ftp",					.has_arg = false,	.val = PROT_FTP},
+	{.name = "http",				.has_arg = false,	.val = PROT_HTTP},
+	{.name = "imap",				.has_arg = false,	.val = PROT_IMAP},
+	{.name = "irc",					.has_arg = false,	.val = PROT_IRC},
+	{.name = "nntp",				.has_arg = false,	.val = PROT_NNTP},
+	{.name = "pop3",				.has_arg = false,	.val = PROT_POP3},
+	{.name = "smb",					.has_arg = false,	.val = PROT_SMB},
+	{.name = "smtp",				.has_arg = false,	.val = PROT_SMTP},
+	{.name = "snmp",				.has_arg = false,	.val = PROT_SNMP},
+	{.name = "ssh",					.has_arg = false,	.val = PROT_SSH},
+	{.name = "ssltls",				.has_arg = false,	.val = PROT_SSLTLS},
+	{.name = "telnet",				.has_arg = false,	.val = PROT_TELNET},
+	{.name = "mssql",				.has_arg = false,	.val = PROT_MSSQL},
+	{.name = "mysql",				.has_arg = false,	.val = PROT_MYSQL},
+	{.name = "oracle",				.has_arg = false,	.val = PROT_ORACLE},
+	{.name = "postgresql",			.has_arg = false,	.val = PROT_POSTGRESQL},
+	{.name = "sybase",				.has_arg = false,	.val = PROT_SYBASE},
+	{.name = "db2",					.has_arg = false,	.val = PROT_DB2},
+	{.name = "informix",			.has_arg = false,	.val = PROT_INFORMIX},
+	{.name = "socks",				.has_arg = false,	.val = TUNL_SOCKS},
+	{.name = "pgpnet",				.has_arg = false,	.val = TUNL_PGPNET},
+	{.name = "httpproxy",			.has_arg = false,	.val = TUNL_HTTPPROXY},
+	{.name = "tor",					.has_arg = false,	.val = TUNL_TOR},
+	{.name = "vnn",					.has_arg = false,	.val = TUNL_VNN},
+	{.name = "softether",			.has_arg = false,	.val = TUNL_SOFTETHER},
+	{.name = "msteredo",			.has_arg = false,	.val = TUNL_MSTEREDO},
+	{.name = "ultrasurf",			.has_arg = false,	.val = TUNL_ULTRASURF},
+	{.name = "hamachi",				.has_arg = false,	.val = TUNL_HAMACHI},
+	{.name = "httptunnel",			.has_arg = false,	.val = TUNL_HTTPTUNNEL},
+	{.name = "pingtunnel",			.has_arg = false,	.val = TUNL_PINGTUNNEL},
+	{.name = "tinyvpn",				.has_arg = false,	.val = TUNL_TINYVPN},
+	{.name = "realtunnel",			.has_arg = false,	.val = TUNL_REALTUNNEL},
+	{.name = "dynapass",			.has_arg = false,	.val = TUNL_DYNAPASS},
+	{.name = "ultravpn",			.has_arg = false,	.val = TUNL_ULTRAVPN},
+	{.name = "freeu",				.has_arg = false,	.val = TUNL_FREEU},
+	{.name = "skyfire",				.has_arg = false,	.val = TUNL_SKYFIRE},
+	{.name = "mms",					.has_arg = false,	.val = STRM_MMS},
+	{.name = "rtsp",				.has_arg = false,	.val = STRM_RTSP},
+	{.name = "tvants",				.has_arg = false,	.val = STRM_TVANTS},
+	{.name = "ppstream",			.has_arg = false,	.val = STRM_PPSTREAM},
+	{.name = "pptv",				.has_arg = false,	.val = STRM_PPLIVE},
+	{.name = "uusee",				.has_arg = false,	.val = STRM_UUSEE},
+	{.name = "nsplayer",			.has_arg = false,	.val = STRM_NSPLAYER},
+	{.name = "pcast",				.has_arg = false,	.val = STRM_PCAST},
+	{.name = "tvkoo",				.has_arg = false,	.val = STRM_TVKOO},
+	{.name = "sopcast",				.has_arg = false,	.val = STRM_SOPCAST},
+	{.name = "udlivex",				.has_arg = false,	.val = STRM_UDLIVEX},
+	{.name = "tvuplayer",			.has_arg = false,	.val = STRM_TVUPLAYER},
+	{.name = "mysee",				.has_arg = false,	.val = STRM_MYSEE},
+	{.name = "joost",				.has_arg = false,	.val = STRM_JOOST},
+	{.name = "flashvideo",			.has_arg = false,	.val = STRM_FLASHVIDEO},
+	{.name = "silverlight",			.has_arg = false,	.val = STRM_SILVERLIGHT},
+	{.name = "slingbox",			.has_arg = false,	.val = STRM_SLINGBOX},
+	{.name = "qvod",				.has_arg = false,	.val = STRM_QVOD},
+	{.name = "qqlive",				.has_arg = false,	.val = STRM_QQLIVE},
+	{.name = "china",				.has_arg = false,	.val = GAME_CNGAMES},
+	{.name = "shanghai",			.has_arg = false,	.val = GAME_SHGAMES},
+	{.name = "lianzhong",			.has_arg = false,	.val = GAME_LZGAMES},
+	{.name = "bianfeng",			.has_arg = false,	.val = GAME_BFGAMES},
+	{.name = "vnc",					.has_arg = false,	.val = RCTL_VNC},
+	{.name = "radmin",				.has_arg = false,	.val = RCTL_RADMIN},
+	{.name = "spyanywhere",			.has_arg = false,	.val = RCTL_SPYANYWHERE},
+	{.name = "showmypc",			.has_arg = false,	.val = RCTL_SHOWMYPC},
+	{.name = "logmein",				.has_arg = false,	.val = RCTL_LOGMEIN},
+	{.name = "teamviewer",			.has_arg = false,	.val = RCTL_TEAMVIEWER},
+	{.name = "gogrok",				.has_arg = false,	.val = RCTL_GOGROK},
+	{.name = "remotecontrolpro",	.has_arg = false,	.val = RCTL_RCONTROLPRO},
+	{.name = "crossloop",			.has_arg = false,	.val = RCTL_CROSSLOOP},
+	{.name = "windowsrdp",			.has_arg = false,	.val = RCTL_WINDOWSRDP},
+	{.name = "pcanywhere",			.has_arg = false,	.val = RCTL_PCANYWHERE},
+	{.name = "timbuktu",			.has_arg = false,	.val = RCTL_TIMBUKTU},
+	{.name = "windowslivesync",		.has_arg = false,	.val = RCTL_WINLIVESYNC},
+	{.name = "sharedview",			.has_arg = false,	.val = RCTL_SHAREDVIEW},
+	{.name = "httpupload",			.has_arg = false,	.val = WHD_HTTPUPLOAD},
+	{.name = "hinetsafebox",		.has_arg = false,	.val = WHD_HINETSAFEBOX},
+	{.name = "msskydrive",			.has_arg = false,	.val = WHD_MSSKYDRIVE},
+	{.name = "gdocuploader",		.has_arg = false,	.val = WHD_GDOCUPLOADER},
+	{.name = "adrive",				.has_arg = false,	.val = WHD_ADRIVE},
+	{.name = "myotherdrive",		.has_arg = false,	.val = WHD_MYOTHERDRIVE},
+	{.name = "mozy",				.has_arg = false,	.val = WHD_MOZY},
+	{.name = "boxnet",				.has_arg = false,	.val = WHD_BOXNET},
+	{.name = "officelive",			.has_arg = false,	.val = WHD_OFFICELIVE},
+	{.name = "readdlestorage",		.has_arg = false,	.val = WHD_READDLESTORAGE},
+	{.name = "dropbox",				.has_arg = false,	.val = WHD_DROPBOX},
+	{NULL},
 };
 
-	
+static const char *const op_cmds[] = {
+	"--ruleidx",
+};
 
-static void
-init(struct xt_entry_match *m)
-{
-    struct ipt_p2p_info *info = (struct ipt_p2p_info *)m->data;
+static const char *const im_adv_cmds[] = {
+	"--msn",
+	"--msnmsg",
+	"--msnfile",
+	"--msngame",
+	"--msnconf",
+	"--msnother",
+	"--ym",
+	"--ymmsg",
+	"--ymfile",
+	"--ymgame",
+	"--ymconf",
+	"--ymother",
+	"--aim",
+	"--aimmsg",
+	"--aimfile",
+	"--aimgame",
+	"--aimconf",
+	"--aimother",
+	"--icq",
+	"--icqmsg",
+	"--icqfile",
+	"--icqgame",
+	"--icqconf",
+	"--icqother",
+};
 
-    /*init the module with default values*/
-    info->cmd = 0;
-    info->debug = 0;
+static const char *const im_cmds[] = {
+	"--wim",
+	"--aim67",
+	"--qq",
+	"--ichat",
+	"--gt",
+	"--gc",
+	"--xfire",
+	"--gadugadu",
+	"--paltalk",
+	"--qnext",
+	"--pocomsg",
+	"--areschat",
+	"--aliww",
+	"--kc",
+	"--lavalava",
+	"--icu2",
+	"--ispq",
+	"--uc",
+	"--mobilemsn",
+	"--baiduhi",
+	"--fetion",
+};
 
-}
-	
+static const char *const voip_cmds[] = {
+	"--skype",
+	"--kubao",
+	"--gizmo",
+	"--siprtp",
+	"--teltel",
+	"--teamspeak",
+};
+
+static const char *const p2p_cmds[] = {
+	"--ss",
+	"--edk",
+	"--ft",
+	"--openft",
+	"--gnu",
+	"--opennap",
+	"--bt",
+	"--xunlei",
+	"--vagaa",
+	"--pp365",
+	"--poco",
+	"--clubbox",
+	"--ares",
+	"--ezpeer",
+	"--pando",
+	"--huntmine",
+	"--kuwo",
+};
+
+static const char *const prot_cmds[] = {
+	"--dns",
+	"--ftp",
+	"--http",
+	"--imap",
+	"--irc",
+	"--nntp",
+	"--pop3",
+	"--smb",
+	"--smtp",
+	"--snmp",
+	"--ssh",
+	"--ssltls",
+	"--telnet",
+	"--mssql",
+	"--mysql",
+	"--oracle",
+	"--postgresql",
+	"--sybase",
+	"--db2",
+	"--informix",
+};
+
+static const char *const tunl_cmds[] = {
+	"--socks",
+	"--pgpnet",
+	"--httpproxy",
+	"--tor",
+	"--vnn",
+	"--softether",
+	"--msteredo",
+	"--ultrasurf",
+	"--hamachi",
+	"--httptunnel",
+	"--pingtunnel",
+	"--tinyvpn",
+	"--realtunnel",
+	"--dynapass",
+	"--ultravpn",
+	"--freeu",
+	"--skyfire",
+};
 
-static int
-parse(int c, char **argv, int invert, unsigned int *flags,
-	const void *entry,
-	struct xt_entry_match **match)
+static const char *const strm_cmds[] = {
+	"--mms",
+	"--rtsp",
+	"--tvants",
+	"--ppstream",
+	"--pptv",
+	"--uusee",
+	"--nsplayer",
+	"--pcast",
+	"--tvkoo",
+	"--sopcast",
+	"--udlivex",
+	"--tvuplayer",
+	"--mysee",
+	"--joost",
+	"--flashvideo",
+	"--silverlight",
+	"--slingbox",
+	"--qvod",
+	"--qqlive",
+};
+
+static const char *const game_cmds[] = {
+	"--china",
+	"--shanghai",
+	"--lianzhong",
+	"--bianfeng",
+};
+
+static const char *const rctl_cmds[] = {
+	"--vnc",
+	"--radmin",
+	"--tvants",
+	"--spyanywhere",
+	"--showmypc",
+	"--logmein",
+	"--teamviewer",
+	"--gogrok",
+	"--remotecontrolpro",
+	"--crossloop",
+	"--windowsrdp",
+	"--pcanywhere",
+	"--windowslivesync",
+	"--sharedview",
+};
+
+static const char *const whd_cmds[] = {
+	"--httpupload",
+	"--hinetsafebox",
+	"--msskydrive",
+	"--gdocuploader",
+	"--adrive",
+	"--myotherdrive",
+	"--mozy",
+	"--boxnet",
+	"--officelive",
+	"--readdlestorage",
+	"--dropbox",
+};
+
+static int ipp2p_mt_parse(int c, char **argv, int invert, unsigned int *flags,
+                          const void *entry, struct xt_entry_match **match)
 {
-    struct ipt_p2p_info *info = (struct ipt_p2p_info *)(*match)->data;
-    
-    switch (c) {
-	case '1':		/*cmd: ipp2p*/
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified once!");
-/*	    if ((*flags & SHORT_HAND_DATA) == SHORT_HAND_DATA)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p-data' may only be "
-				"specified alone!");*/
-	    if ((*flags) != 0)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-	    *flags += SHORT_HAND_IPP2P;
-	    info->cmd = *flags;
-	    break;
-	    
-	case '2':		/*cmd: edk*/
-	    if ((*flags & IPP2P_EDK) == IPP2P_EDK)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--edk' may only be "
-				"specified once");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-/*	    if ((*flags & SHORT_HAND_DATA) == SHORT_HAND_DATA)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p-data' may only be "
-				"specified alone!");*/
-            if ((*flags & IPP2P_DATA_EDK) == IPP2P_DATA_EDK)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: use `--edk' OR `--edk-data' but not both of them!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-	    *flags += IPP2P_EDK;
-	    info->cmd = *flags;	    
-	    break;
-
-
-	case '7':		/*cmd: dc*/
-            if ((*flags & IPP2P_DC) == IPP2P_DC)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--dc' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-/*	    if ((*flags & SHORT_HAND_DATA) == SHORT_HAND_DATA)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p-data' may only be "
-				"specified alone!");*/
-            if ((*flags & IPP2P_DATA_DC) == IPP2P_DATA_DC)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: use `--dc' OR `--dc-data' but not both of them!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_DC;
-	    info->cmd = *flags;
-	    break;
-
-
-	case '9':		/*cmd: gnu*/
-            if ((*flags & IPP2P_GNU) == IPP2P_GNU)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--gnu' may only be "
-                                "specified once!");
-/*	    if ((*flags & SHORT_HAND_DATA) == SHORT_HAND_DATA)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p-data' may only be "
-				"specified alone!");*/
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-            if ((*flags & IPP2P_DATA_GNU) == IPP2P_DATA_GNU)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: use `--gnu' OR `--gnu-data' but not both of them!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_GNU;
-	    info->cmd = *flags;
-	    break;
-
-	case 'a':		/*cmd: kazaa*/
-            if ((*flags & IPP2P_KAZAA) == IPP2P_KAZAA)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--kazaa' may only be "
-                                "specified once!");
-/*	    if ((*flags & SHORT_HAND_DATA) == SHORT_HAND_DATA)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p-data' may only be "
-				"specified alone!");*/
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-            if ((*flags & IPP2P_DATA_KAZAA) == IPP2P_DATA_KAZAA)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: use `--kazaa' OR `--kazaa-data' but not both of them!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_KAZAA;
-	    info->cmd = *flags;
-	    break;																											
-
-	case 'b':		/*cmd: bit*/
-            if ((*flags & IPP2P_BIT) == IPP2P_BIT)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--bit' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_BIT;
-	    info->cmd = *flags;
-	    break;																											
-
-	case 'c':		/*cmd: apple*/
-            if ((*flags & IPP2P_APPLE) == IPP2P_APPLE)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--apple' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_APPLE;
-	    info->cmd = *flags;
-	    break;																											
-
-
-	case 'd':		/*cmd: soul*/
-            if ((*flags & IPP2P_SOUL) == IPP2P_SOUL)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--soul' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_SOUL;
-	    info->cmd = *flags;
-	    break;																											
-
-
-	case 'e':		/*cmd: winmx*/
-            if ((*flags & IPP2P_WINMX) == IPP2P_WINMX)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--winmx' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_WINMX;
-	    info->cmd = *flags;
-	    break;																											
-
-	case 'f':		/*cmd: ares*/
-            if ((*flags & IPP2P_ARES) == IPP2P_ARES)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--ares' may only be "
-                                "specified once!");
-	    if ((*flags & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P)
-		    exit_error(PARAMETER_PROBLEM,
-				"ipp2p: `--ipp2p' may only be "
-				"specified alone!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_ARES;
-	    info->cmd = *flags;
-	    break;																											
+	struct ipt_appe_info *info = (struct ipt_appe_info *)(*match)->data;
+	int ap_type = GET_AP_TYPE(c);
+	int ap_subgroup = GET_AP_SUBGROUP(c);
+	int ap_enable = GET_AP_ENABLE(c);
+
+	// operation command
+	if ((c == CMD_RULE_INDEX) && (argv[optind - 1] != NULL)) {
+		*flags |= CMD_RULE_INDEX;
+		info->rule_idx = atoi(argv[optind - 1]);
+		return 1;
+	}
+
+	// apid config
+	switch (ap_type) {
+		case APPE_TYPE_IM_ADV:
+			*flags |= ap_enable;
+			info->im_adv_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_IM:
+			*flags |= ap_enable;
+			info->im_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_VOIP:
+			*flags |= ap_enable;
+			info->voip_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_P2P:
+			*flags |= ap_enable;
+			info->p2p_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_PROT:
+			*flags |= ap_enable;
+			info->prot_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_TUNL:
+			*flags |= ap_enable;
+			info->tunl_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_STRM:
+			*flags |= ap_enable;
+			info->strm_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_GAME:
+			*flags |= ap_enable;
+			info->game_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_RCTL:
+			*flags |= ap_enable;
+			info->rctl_cfg[ap_subgroup] |= ap_enable;
+			break;
+		case APPE_TYPE_WHD:
+			*flags |= ap_enable;
+			info->whd_cfg[ap_subgroup] |= ap_enable;
+			break;
+		default:
+			return 0;
+	}
 	
-	case 'g':		/*cmd: mute*/
-            if ((*flags & IPP2P_MUTE) == IPP2P_MUTE)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--mute' may only be "
-                                "specified once!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_MUTE;
-	    info->cmd = *flags;
-	    break;																											
-	case 'h':		/*cmd: waste*/
-            if ((*flags & IPP2P_WASTE) == IPP2P_WASTE)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--waste' may only be "
-                                "specified once!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_WASTE;
-	    info->cmd = *flags;
-	    break;																											
-	case 'i':		/*cmd: xdcc*/
-            if ((*flags & IPP2P_XDCC) == IPP2P_XDCC)
-            exit_error(PARAMETER_PROBLEM,
-                                "ipp2p: `--ares' may only be "
-                                "specified once!");
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-            *flags += IPP2P_XDCC;
-	    info->cmd = *flags;
-	    break;																											
-
-	case 'j':		/*cmd: debug*/
-	    if (invert) exit_error(PARAMETER_PROBLEM, "ipp2p: invert [!] is not allowed!");
-	    info->debug = 1;
-	    break;																											
-
-	default:
-//            exit_error(PARAMETER_PROBLEM,
-//	    "\nipp2p-parameter problem: for ipp2p usage type: iptables -m ipp2p --help\n");
-	    return 0;
-    }
-    return 1;
+	return 1;
 }
 
-
-static void
-final_check(unsigned int flags)
+static void ipp2p_mt_check(unsigned int flags)
 {
-    if (!flags)
-            exit_error(PARAMETER_PROBLEM,
-	    "\nipp2p-parameter problem: for ipp2p usage type: iptables -m ipp2p --help\n");
+	if (!flags)
+		exit_error(PARAMETER_PROBLEM,
+			"\nipp2p-parameter problem: for ipp2p usage type: iptables -m ipp2p --help\n");
 }
 
+static void ipp2p_mt_print_cmd(int *cfg, const char **const cmds, int from, int to)
+{
+	unsigned int i;
 
+	for (i = from; i <= to; ++i)
+		if (cfg[GET_AP_SUBGROUP(i)] & GET_AP_ENABLE(i))
+			printf("%s ", cmds[GET_AP_INDEX(i)]);
+}
 
-static void
-print(const void *ip,
-        const struct xt_entry_match *match,
-	int numeric)
+static void ipp2p_mt_print_detail(const void *entry, const struct xt_entry_match *match, int numeric)
 {
-    struct ipt_p2p_info *info = (struct ipt_p2p_info *)match->data;
-    
-    printf("ipp2p v%s", IPP2P_VERSION);
-    if ((info->cmd & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P) printf(" --ipp2p");
-//    if ((info->cmd & SHORT_HAND_DATA) == SHORT_HAND_DATA) printf(" --ipp2p-data");
-    if ((info->cmd & IPP2P_KAZAA) == IPP2P_KAZAA) printf(" --kazaa");
-//    if ((info->cmd & IPP2P_DATA_KAZAA) == IPP2P_DATA_KAZAA) printf(" --kazaa-data");
-//    if ((info->cmd & IPP2P_DATA_GNU) == IPP2P_DATA_GNU) printf(" --gnu-data");
-    if ((info->cmd & IPP2P_GNU) == IPP2P_GNU) printf(" --gnu");
-    if ((info->cmd & IPP2P_EDK) == IPP2P_EDK) printf(" --edk");
-//    if ((info->cmd & IPP2P_DATA_EDK) == IPP2P_DATA_EDK) printf(" --edk-data");
-//    if ((info->cmd & IPP2P_DATA_DC) == IPP2P_DATA_DC) printf(" --dc-data");
-    if ((info->cmd & IPP2P_DC) == IPP2P_DC) printf(" --dc");
-    if ((info->cmd & IPP2P_BIT) == IPP2P_BIT) printf(" --bit");
-    if ((info->cmd & IPP2P_APPLE) == IPP2P_APPLE) printf(" --apple");
-    if ((info->cmd & IPP2P_SOUL) == IPP2P_SOUL) printf(" --soul");
-    if ((info->cmd & IPP2P_WINMX) == IPP2P_WINMX) printf(" --winmx");
-    if ((info->cmd & IPP2P_ARES) == IPP2P_ARES) printf(" --ares");
-    if ((info->cmd & IPP2P_MUTE) == IPP2P_MUTE) printf(" --mute");
-    if ((info->cmd & IPP2P_WASTE) == IPP2P_WASTE) printf(" --waste");
-    if ((info->cmd & IPP2P_XDCC) == IPP2P_XDCC) printf(" --xdcc");
-    if (info->debug != 0) printf(" --debug");
-    printf(" ");
+	const struct ipt_appe_info *info = (const void *)match->data;
+	unsigned int i;
+	int ap_subgroup;
+	int ap_enable;
+
+	printf("%s %d ", op_cmds[GET_AP_INDEX(CMD_RULE_INDEX)], info->rule_idx);
+	ipp2p_mt_print_cmd(info->im_adv_cfg, im_adv_cmds, IM_MSN, IM_ICQ_OTHER);
+	ipp2p_mt_print_cmd(info->im_cfg, im_cmds, IM_WEB, IM_FETION);
+	ipp2p_mt_print_cmd(info->voip_cfg, voip_cmds, VOIP_SKYPE, VOIP_TEAMSPEAK);
+	ipp2p_mt_print_cmd(info->p2p_cfg, p2p_cmds, P2P_SOULSEEK, P2P_KUWO);
+	ipp2p_mt_print_cmd(info->prot_cfg, prot_cmds, PROT_DNS, PROT_INFORMIX);
+	ipp2p_mt_print_cmd(info->tunl_cfg, tunl_cmds, TUNL_SOCKS, TUNL_SKYFIRE);
+	ipp2p_mt_print_cmd(info->strm_cfg, strm_cmds, STRM_MMS, STRM_QQLIVE);
+	ipp2p_mt_print_cmd(info->game_cfg, game_cmds, GAME_CNGAMES, GAME_BFGAMES);
+	ipp2p_mt_print_cmd(info->rctl_cfg, rctl_cmds, RCTL_VNC, RCTL_SHAREDVIEW);
+	ipp2p_mt_print_cmd(info->whd_cfg, whd_cmds, WHD_HTTPUPLOAD, WHD_DROPBOX);
 }
-			    						   
 
-
-static void
-save(const void *ip, const struct xt_entry_match *match)
+static void ipp2p_mt_print(const void *entry,
+    const struct xt_entry_match *match, int numeric)
 {
-    struct ipt_p2p_info *info = (struct ipt_p2p_info *)match->data;
-    
-    if ((info->cmd & SHORT_HAND_IPP2P) == SHORT_HAND_IPP2P) printf("--ipp2p ");
-//    if ((info->cmd & SHORT_HAND_DATA) == SHORT_HAND_DATA) printf("--ipp2p-data ");
-    if ((info->cmd & IPP2P_KAZAA) == IPP2P_KAZAA) printf("--kazaa ");
-//    if ((info->cmd & IPP2P_DATA_KAZAA) == IPP2P_DATA_KAZAA) printf("--kazaa-data ");
-//    if ((info->cmd & IPP2P_DATA_GNU) == IPP2P_DATA_GNU) printf("--gnu-data ");
-    if ((info->cmd & IPP2P_GNU) == IPP2P_GNU) printf("--gnu ");
-    if ((info->cmd & IPP2P_EDK) == IPP2P_EDK) printf("--edk ");
-//    if ((info->cmd & IPP2P_DATA_EDK) == IPP2P_DATA_EDK) printf("--edk-data ");
-//    if ((info->cmd & IPP2P_DATA_DC) == IPP2P_DATA_DC) printf("--dc-data ");
-    if ((info->cmd & IPP2P_DC) == IPP2P_DC) printf("--dc ");
-    if ((info->cmd & IPP2P_BIT) == IPP2P_BIT) printf("--bit ");
-    if ((info->cmd & IPP2P_APPLE) == IPP2P_APPLE) printf("--apple ");
-    if ((info->cmd & IPP2P_SOUL) == IPP2P_SOUL) printf("--soul ");
-    if ((info->cmd & IPP2P_WINMX) == IPP2P_WINMX) printf("--winmx ");
-    if ((info->cmd & IPP2P_ARES) == IPP2P_ARES) printf("--ares ");
-    if ((info->cmd & IPP2P_MUTE) == IPP2P_MUTE) printf(" --mute");
-    if ((info->cmd & IPP2P_WASTE) == IPP2P_WASTE) printf(" --waste");
-    if ((info->cmd & IPP2P_XDCC) == IPP2P_XDCC) printf(" --xdcc");
-    if (info->debug != 0) printf("--debug ");
+	printf("ipp2p ");
+	ipp2p_mt_print_detail(entry, match, true);
 }
 
-		
-
+static void ipp2p_mt_save(const void *entry, const struct xt_entry_match *match)
+{
+	ipp2p_mt_print_detail(entry, match, true);
+}
 
-static 
-struct iptables_match ipp2p= 
-{ 
-    .family         = AF_INET,	
-    .name           = "ipp2p",
-    .version        = IPTABLES_VERSION,
-    .size           = IPT_ALIGN(sizeof(struct ipt_p2p_info)),
-    .userspacesize  = IPT_ALIGN(sizeof(struct ipt_p2p_info)),
-    .help           = help,
-    .init           = init,
-    .parse          = parse,
-    .final_check    = final_check,
-    .print          = print,
-    .save           = save,
-    .extra_opts     = opts
+static struct xtables_match ipp2p_mt_reg = {
+	.version       = IPTABLES_VERSION,
+	.name          = "ipp2p",
+	.revision      = 1,
+	.family        = AF_INET,
+	.size          = XT_ALIGN(sizeof(struct ipt_appe_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct ipt_appe_info)),
+	.help          = ipp2p_mt_help,
+	.parse         = ipp2p_mt_parse,
+	.final_check   = ipp2p_mt_check,
+	.print         = ipp2p_mt_print,
+	.save          = ipp2p_mt_save,
+	.extra_opts    = ipp2p_mt_opts,
 };
-					    
 
-
-void _init(void)
+static __attribute__((constructor)) void ipp2p_mt_ldr(void)
 {
-    register_match(&ipp2p);
+	xtables_register_match(&ipp2p_mt_reg);
 }
-
