diff -Nru linux-2.4.20/Documentation/Configure.help linux-2.4.20-pom2patch/Documentation/Configure.help
--- linux-2.4.20/Documentation/Configure.help	2003-05-02 12:57:31.000000000 -0500
+++ linux-2.4.20-pom2patch/Documentation/Configure.help	2003-05-02 12:57:39.000000000 -0500
@@ -2519,6 +2519,16 @@
   If you want to compile it as a module, say M here and read
   <file:Documentation/modules.txt>.  If unsure, say `Y'.
 
+MMS protocol support
+CONFIG_IP_NF_MMS
+  Tracking MMS (Microsoft Windows Media Services) connections
+  could be problematic if random ports are used to send the
+  streaming content. This option allows users to track streaming
+  connections over random UDP or TCP ports.
+
+  If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.  If unsure, say `Y'.
+
 IRC Send/Chat protocol support
 CONFIG_IP_NF_IRC
   There is a commonly-used extension to IRC called
diff -Nru linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.20-pom2patch/include/linux/netfilter_ipv4/ip_conntrack.h
--- linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack.h	2003-05-02 12:57:21.000000000 -0500
+++ linux-2.4.20-pom2patch/include/linux/netfilter_ipv4/ip_conntrack.h	2003-05-02 12:57:39.000000000 -0500
@@ -58,6 +58,7 @@
 };
 
 /* Add protocol helper include file here */
+#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
 
 #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
@@ -66,6 +67,7 @@
 /* per expectation: application helper private data */
 union ip_conntrack_expect_help {
 	/* insert conntrack helper private data (expect) here */
+	struct ip_ct_mms_expect exp_mms_info;
 	struct ip_ct_h225_expect exp_h225_info;
 	struct ip_ct_ftp_expect exp_ftp_info;
 	struct ip_ct_irc_expect exp_irc_info;
@@ -80,6 +82,7 @@
 /* per conntrack: application helper private data */
 union ip_conntrack_help {
 	/* insert conntrack helper private data (master) here */
+	struct ip_ct_mms_master ct_mms_info;
 	struct ip_ct_h225_master ct_h225_info;
 	struct ip_ct_ftp_master ct_ftp_info;
 	struct ip_ct_irc_master ct_irc_info;
diff -Nru linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_mms.h linux-2.4.20-pom2patch/include/linux/netfilter_ipv4/ip_conntrack_mms.h
--- linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_mms.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.4.20-pom2patch/include/linux/netfilter_ipv4/ip_conntrack_mms.h	2003-05-02 12:57:39.000000000 -0500
@@ -0,0 +1,31 @@
+#ifndef _IP_CONNTRACK_MMS_H
+#define _IP_CONNTRACK_MMS_H
+/* MMS tracking. */
+
+#ifdef __KERNEL__
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+DECLARE_LOCK_EXTERN(ip_mms_lock);
+
+#define MMS_PORT                         1755
+#define MMS_SRV_MSG_ID                   196610
+
+#define MMS_SRV_MSG_OFFSET               36
+#define MMS_SRV_UNICODE_STRING_OFFSET    60
+#define MMS_SRV_CHUNKLENLV_OFFSET        16
+#define MMS_SRV_CHUNKLENLM_OFFSET        32
+#define MMS_SRV_MESSAGELENGTH_OFFSET     8
+#endif
+
+/* This structure is per expected connection */
+struct ip_ct_mms_expect {
+	u_int32_t len;
+	u_int32_t padding;
+	u_int16_t port;
+};
+
+/* This structure exists only once per master */
+struct ip_ct_mms_master {
+};
+
+#endif /* _IP_CONNTRACK_MMS_H */
diff -Nru linux-2.4.20/net/ipv4/netfilter/Config.in linux-2.4.20-pom2patch/net/ipv4/netfilter/Config.in
--- linux-2.4.20/net/ipv4/netfilter/Config.in	2003-05-02 12:57:31.000000000 -0500
+++ linux-2.4.20-pom2patch/net/ipv4/netfilter/Config.in	2003-05-02 12:57:39.000000000 -0500
@@ -11,6 +11,7 @@
   dep_tristate '  Eggdrop bot support' CONFIG_IP_NF_EGG $CONFIG_IP_NF_CONNTRACK
   bool '  Connection mark tracking support' CONFIG_IP_NF_CONNTRACK_MARK
   dep_tristate '  IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK
+  dep_tristate '  MMS protocol support' CONFIG_IP_NF_MMS $CONFIG_IP_NF_CONNTRACK
   dep_tristate '  CuSeeMe protocol support' CONFIG_IP_NF_CUSEEME $CONFIG_IP_NF_CONNTRACK
 fi
 
@@ -104,6 +105,13 @@
           define_tristate CONFIG_IP_NF_NAT_IRC $CONFIG_IP_NF_NAT
         fi
       fi
+      if [ "$CONFIG_IP_NF_MMS" = "m" ]; then
+        define_tristate CONFIG_IP_NF_NAT_MMS m
+      else
+        if [ "$CONFIG_IP_NF_MMS" = "y" ]; then
+          define_tristate CONFIG_IP_NF_NAT_MMS $CONFIG_IP_NF_NAT
+        fi
+      fi
       if [ "$CONFIG_IP_NF_CUSEEME" = "m" ]; then
         define_tristate CONFIG_IP_NF_NAT_CUSEEME m
       else
diff -Nru linux-2.4.20/net/ipv4/netfilter/Makefile linux-2.4.20-pom2patch/net/ipv4/netfilter/Makefile
--- linux-2.4.20/net/ipv4/netfilter/Makefile	2003-05-02 12:57:31.000000000 -0500
+++ linux-2.4.20-pom2patch/net/ipv4/netfilter/Makefile	2003-05-02 12:57:39.000000000 -0500
@@ -40,6 +40,10 @@
 
 
 # connection tracking helpers
+obj-$(CONFIG_IP_NF_MMS) += ip_conntrack_mms.o
+ifdef CONFIG_IP_NF_MMS
+	export-objs += ip_conntrack_mms.o
+endif
 obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
 obj-$(CONFIG_IP_NF_EGG) += ip_conntrack_egg.o
 
@@ -56,6 +60,7 @@
 obj-$(CONFIG_IP_NF_NAT_CUSEEME) += ip_nat_cuseeme.o
 obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
 obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
+obj-$(CONFIG_IP_NF_NAT_MMS) += ip_nat_mms.o
 
 # generic IP tables 
 obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
diff -Nru linux-2.4.20/net/ipv4/netfilter/ip_conntrack_mms.c linux-2.4.20-pom2patch/net/ipv4/netfilter/ip_conntrack_mms.c
--- linux-2.4.20/net/ipv4/netfilter/ip_conntrack_mms.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.4.20-pom2patch/net/ipv4/netfilter/ip_conntrack_mms.c	2003-05-02 12:57:39.000000000 -0500
@@ -0,0 +1,308 @@
+/* MMS extension for IP connection tracking
+ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
+ * based on ip_conntrack_ftp.c and ip_conntrack_irc.c
+ *
+ * ip_conntrack_mms.c v0.3 2002-09-22
+ *
+ *      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.
+ *
+ *      Module load syntax:
+ *      insmod ip_conntrack_mms.o ports=port1,port2,...port<MAX_PORTS>
+ *
+ *      Please give the ports of all MMS servers You wish to connect to.
+ *      If you don't specify ports, the default will be TCP port 1755.
+ *
+ *      More info on MMS protocol, firewalls and NAT:
+ *      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp
+ *      http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp
+ *
+ *      The SDP project people are reverse-engineering MMS:
+ *      http://get.to/sdp
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/ctype.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
+
+DECLARE_LOCK(ip_mms_lock);
+struct module *ip_conntrack_mms = THIS_MODULE;
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int ports_c;
+#ifdef MODULE_PARM
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
+#endif
+
+#if 0 
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+EXPORT_SYMBOL(ip_mms_lock);
+
+MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
+MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) connection tracking module");
+MODULE_LICENSE("GPL");
+
+/* #define isdigit(c) (c >= '0' && c <= '9') */
+
+/* copied from drivers/usb/serial/io_edgeport.c - not perfect but will do the trick */
+static void unicode_to_ascii (char *string, short *unicode, int unicode_size)
+{
+	int i;
+	for (i = 0; i < unicode_size; ++i) {
+		string[i] = (char)(unicode[i]);
+	}
+	string[unicode_size] = 0x00;
+}
+
+__inline static int atoi(char *s) 
+{
+	int i=0;
+	while (isdigit(*s)) {
+		i = i*10 + *(s++) - '0';
+	}
+	return i;
+}
+
+/* convert ip address string like "192.168.0.10" to unsigned int */
+__inline static u_int32_t asciiiptoi(char *s)
+{
+	unsigned int i, j, k;
+
+	for(i=k=0; k<3; ++k, ++s, i<<=8) {
+		i+=atoi(s);
+		for(j=0; (*(++s) != '.') && (j<3); ++j)
+			;
+	}
+	i+=atoi(s);
+	return ntohl(i);
+}
+
+int parse_mms(const char *data, 
+	      const unsigned int datalen,
+	      u_int32_t *mms_ip,
+	      u_int16_t *mms_proto,
+	      u_int16_t *mms_port,
+	      char **mms_string_b,
+	      char **mms_string_e,
+	      char **mms_padding_e)
+{
+	int unicode_size, i;
+	char tempstring[28];       /* "\\255.255.255.255\UDP\65535" */
+	char getlengthstring[28];
+	
+	for(unicode_size=0; 
+	    (char) *(data+(MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2)) != (char)0;
+	    unicode_size++)
+		if ((unicode_size == 28) || (MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2 >= datalen)) 
+			return -1; /* out of bounds - incomplete packet */
+	
+	unicode_to_ascii(tempstring, (short *)(data+MMS_SRV_UNICODE_STRING_OFFSET), unicode_size);
+	DEBUGP("ip_conntrack_mms: offset 60: %s\n", (const char *)(tempstring));
+	
+	/* IP address ? */
+	*mms_ip = asciiiptoi(tempstring+2);
+	
+	i=sprintf(getlengthstring, "%u.%u.%u.%u", HIPQUAD(*mms_ip));
+		
+	/* protocol ? */
+	if(strncmp(tempstring+3+i, "TCP", 3)==0)
+		*mms_proto = IPPROTO_TCP;
+	else if(strncmp(tempstring+3+i, "UDP", 3)==0)
+		*mms_proto = IPPROTO_UDP;
+
+	/* port ? */
+	*mms_port = atoi(tempstring+7+i);
+
+	/* we store a pointer to the beginning of the "\\a.b.c.d\proto\port" 
+	   unicode string, one to the end of the string, and one to the end 
+	   of the packet, since we must keep track of the number of bytes 
+	   between end of the unicode string and the end of packet (padding) */
+	*mms_string_b  = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET);
+	*mms_string_e  = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET + unicode_size * 2);
+	*mms_padding_e = (char *)(data + datalen); /* looks funny, doesn't it */
+	return 0;
+}
+
+
+/* FIXME: This should be in userspace.  Later. */
+static int help(const struct iphdr *iph, size_t len,
+		struct ip_conntrack *ct,
+		enum ip_conntrack_info ctinfo)
+{
+	/* tcplen not negative guaranteed by ip_conntrack_tcp.c */
+	struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+	const char *data = (const char *)tcph + tcph->doff * 4;
+	unsigned int tcplen = len - iph->ihl * 4;
+	unsigned int datalen = tcplen - tcph->doff * 4;
+	int dir = CTINFO2DIR(ctinfo);
+	struct ip_conntrack_expect expect, *exp = &expect; 
+	struct ip_ct_mms_expect *exp_mms_info = &exp->help.exp_mms_info;
+	
+	u_int32_t mms_ip;
+	u_int16_t mms_proto;
+	char mms_proto_string[8];
+	u_int16_t mms_port;
+	char *mms_string_b, *mms_string_e, *mms_padding_e;
+	     
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED
+	    && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
+		DEBUGP("ip_conntrack_mms: Conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Not whole TCP header? */
+	if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
+		DEBUGP("ip_conntrack_mms: tcplen = %u\n", (unsigned)tcplen);
+		return NF_ACCEPT;
+	}
+
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+	    csum_partial((char *)tcph, tcplen, 0))) {
+		DEBUGP("mms_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr),
+		       NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+	
+	/* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP payload */
+	/* FIXME: There is an issue with only looking at this packet: before this packet, 
+	   the client has already sent a packet to the server with the server's hostname 
+	   according to the client (think of it as the "Host: " header in HTTP/1.1). The 
+	   server will break the connection if this doesn't correspond to its own host 
+	   header. The client can also connect to an IP address; if it's the server's IP
+	   address, it will not break the connection. When doing DNAT on a connection 
+	   where the client uses a server's IP address, the nat module should detect
+	   this and change this string accordingly to the DNATed address. This should
+	   probably be done by checking for an IP address, then storing it as a member
+	   of struct ip_ct_mms_expect and checking for it in ip_nat_mms...
+	   */
+	if( (MMS_SRV_MSG_OFFSET < datalen) && 
+	    ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) {
+		DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", 
+		       (u8)*(data+36), (u8)*(data+37), 
+		       (u8)*(data+38), (u8)*(data+39),
+		       datalen);
+		if(parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port,
+		             &mms_string_b, &mms_string_e, &mms_padding_e))
+			if(net_ratelimit())
+				/* FIXME: more verbose debugging ? */
+				printk(KERN_WARNING
+				       "ip_conntrack_mms: Unable to parse data payload\n");
+
+		memset(&expect, 0, sizeof(expect));
+
+		sprintf(mms_proto_string, "(%u)", mms_proto);
+		DEBUGP("ip_conntrack_mms: adding %s expectation %u.%u.%u.%u -> %u.%u.%u.%u:%u\n",
+		       mms_proto == IPPROTO_TCP ? "TCP"
+		       : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string,
+		       NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
+		       NIPQUAD(mms_ip),
+		       mms_port);
+		
+		/* it's possible that the client will just ask the server to tunnel
+		   the stream over the same TCP session (from port 1755): there's 
+		   shouldn't be a need to add an expectation in that case, but it
+		   makes NAT packet mangling so much easier */
+		LOCK_BH(&ip_mms_lock);
+
+		DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq);
+		
+		exp->seq = ntohl(tcph->seq) + (mms_string_b - data);
+		exp_mms_info->len     = (mms_string_e  - mms_string_b);
+		exp_mms_info->padding = (mms_padding_e - mms_string_e);
+		exp_mms_info->port    = mms_port;
+		
+		DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), len=%d, padding=%u\n",
+		       exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding);
+		
+		exp->tuple = ((struct ip_conntrack_tuple)
+		              { { ct->tuplehash[!dir].tuple.src.ip, { 0 } },
+		              { mms_ip,
+		                { (__u16) ntohs(mms_port) },
+		                mms_proto } }
+		             );
+		exp->mask  = ((struct ip_conntrack_tuple)
+		             { { 0xFFFFFFFF, { 0 } },
+		               { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
+		exp->expectfn = NULL;
+		ip_conntrack_expect_related(ct, &expect);
+		UNLOCK_BH(&ip_mms_lock);
+	}
+
+	return NF_ACCEPT;
+}
+
+static struct ip_conntrack_helper mms[MAX_PORTS];
+static char mms_names[MAX_PORTS][10];
+
+/* Not __exit: called from init() */
+static void fini(void)
+{
+	int i;
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		DEBUGP("ip_conntrack_mms: unregistering helper for port %d\n",
+				ports[i]);
+		ip_conntrack_helper_unregister(&mms[i]);
+	}
+}
+
+static int __init init(void)
+{
+	int i, ret;
+	char *tmpname;
+
+	if (ports[0] == 0)
+		ports[0] = MMS_PORT;
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		memset(&mms[i], 0, sizeof(struct ip_conntrack_helper));
+		mms[i].tuple.src.u.tcp.port = htons(ports[i]);
+		mms[i].tuple.dst.protonum = IPPROTO_TCP;
+		mms[i].mask.src.u.tcp.port = 0xFFFF;
+		mms[i].mask.dst.protonum = 0xFFFF;
+		mms[i].max_expected = 1;
+		mms[i].timeout = 0;
+		mms[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
+		mms[i].me = THIS_MODULE;
+		mms[i].help = help;
+
+		tmpname = &mms_names[i][0];
+		if (ports[i] == MMS_PORT)
+			sprintf(tmpname, "mms");
+		else
+			sprintf(tmpname, "mms-%d", ports[i]);
+		mms[i].name = tmpname;
+
+		DEBUGP("ip_conntrack_mms: registering helper for port %d\n", 
+				ports[i]);
+		ret = ip_conntrack_helper_register(&mms[i]);
+
+		if (ret) {
+			fini();
+			return ret;
+		}
+		ports_c++;
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff -Nru linux-2.4.20/net/ipv4/netfilter/ip_nat_mms.c linux-2.4.20-pom2patch/net/ipv4/netfilter/ip_nat_mms.c
--- linux-2.4.20/net/ipv4/netfilter/ip_nat_mms.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.4.20-pom2patch/net/ipv4/netfilter/ip_nat_mms.c	2003-05-02 12:57:39.000000000 -0500
@@ -0,0 +1,350 @@
+/* MMS extension for TCP NAT alteration.
+ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
+ * based on ip_nat_ftp.c and ip_nat_irc.c
+ *
+ * ip_nat_mms.c v0.3 2002-09-22
+ *
+ *      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.
+ *
+ *      Module load syntax:
+ *      insmod ip_nat_mms.o ports=port1,port2,...port<MAX_PORTS>
+ *
+ *      Please give the ports of all MMS servers You wish to connect to.
+ *      If you don't specify ports, the default will be TCP port 1755.
+ *
+ *      More info on MMS protocol, firewalls and NAT:
+ *      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp
+ *      http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp
+ *
+ *      The SDP project people are reverse-engineering MMS:
+ *      http://get.to/sdp
+ */
+
+/* FIXME: issue with UDP & fragmentation with this URL: 
+   http://www.cnn.com/video/world/2002/01/21/jb.shoe.bomb.cafe.cnn.low.asx 
+   may be related to out-of-order first packets:
+   basically the expectation is set up correctly, then the server sends
+   a first UDP packet which is fragmented plus arrives out-of-order.
+   the MASQUERADING firewall with ip_nat_mms loaded responds with
+   an ICMP unreachable back to the server */
+
+#include <linux/module.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+
+#if 0 
+#define DEBUGP printk
+#define DUMP_BYTES(address, counter)                                \
+({                                                                  \
+	int temp_counter;                                           \
+	for(temp_counter=0; temp_counter<counter; ++temp_counter) { \
+		DEBUGP("%u ", (u8)*(address+temp_counter));         \
+	};                                                          \
+	DEBUGP("\n");                                               \
+})
+#else
+#define DEBUGP(format, args...)
+#define DUMP_BYTES(address, counter)
+#endif
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int ports_c = 0;
+
+#ifdef MODULE_PARM
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
+#endif
+
+MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
+MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) NAT module");
+MODULE_LICENSE("GPL");
+
+DECLARE_LOCK_EXTERN(ip_mms_lock);
+
+/* FIXME: Time out? --RR */
+
+static int mms_data_fixup(const struct ip_ct_mms_expect *ct_mms_info,
+                          struct ip_conntrack *ct,
+                          struct sk_buff **pskb,
+                          enum ip_conntrack_info ctinfo,
+                          struct ip_conntrack_expect *expect)
+{
+	u_int32_t newip;
+	struct ip_conntrack_tuple t;
+	struct iphdr *iph = (*pskb)->nh.iph;
+	struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
+	char *data = (char *)tcph + tcph->doff * 4;
+	int i, j, k, port;
+	u_int16_t mms_proto;
+
+	u_int32_t *mms_chunkLenLV    = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET);
+	u_int32_t *mms_chunkLenLM    = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET);
+	u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET);
+
+	int zero_padding;
+
+	char buffer[28];         /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */
+	char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */
+	char proto_string[6];
+	
+	MUST_BE_LOCKED(&ip_mms_lock);
+
+	/* what was the protocol again ? */
+	mms_proto = expect->tuple.dst.protonum;
+	sprintf(proto_string, "%u", mms_proto);
+	
+	DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) in %u, proto %s\n",
+	       expect->seq, ct_mms_info->len, ntohl(tcph->seq),
+	       mms_proto == IPPROTO_UDP ? "UDP"
+	       : mms_proto == IPPROTO_TCP ? "TCP":proto_string);
+	
+	newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+
+	/* Alter conntrack's expectations. */
+	t = expect->tuple;
+	t.dst.ip = newip;
+	for (port = ct_mms_info->port; port != 0; port++) {
+		t.dst.u.tcp.port = htons(port);
+		if (ip_conntrack_change_expect(expect, &t) == 0) {
+			DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port);
+			break;
+		}
+	}
+	
+	if(port == 0)
+		return 0;
+
+	sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u",
+	        NIPQUAD(newip),
+		expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP"
+		: expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string,
+		port);
+	DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer);
+	
+	memset(unicode_buffer, 0, sizeof(char)*75);
+
+	for (i=0; i<strlen(buffer); ++i)
+		*(unicode_buffer+i*2)=*(buffer+i);
+	
+	DEBUGP("ip_nat_mms: mms_data_fixup: padding: %u len: %u\n", ct_mms_info->padding, ct_mms_info->len);
+	DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len);
+	DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60);
+	
+	/* add end of packet to it */
+	for (j=0; j<ct_mms_info->padding; ++j) {
+		DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n", 
+		       i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j));
+		*(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j);
+	}
+
+	/* pad with zeroes at the end ? see explanation of weird math below */
+	zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8;
+	for (k=0; k<zero_padding; ++k)
+		*(unicode_buffer+i*2+j+k)= (char)0;
+	
+	DEBUGP("ip_nat_mms: mms_data_fixup: zero_padding = %u\n", zero_padding);
+	DEBUGP("ip_nat_mms: original=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
+	       *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
+	
+	/* explanation, before I forget what I did:
+	   strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8;
+	   divide by 8 and add 3 to compute the mms_chunkLenLM field,
+	   but note that things may have to be padded with zeroes to align by 8 
+	   bytes, hence we add 7 and divide by 8 to get the correct length */ 
+	*mms_chunkLenLM    = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8);
+	*mms_chunkLenLV    = *mms_chunkLenLM+2;
+	*mms_messageLength = *mms_chunkLenLV*8;
+	
+	DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
+	       *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
+	
+	ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
+	                         expect->seq - ntohl(tcph->seq),
+	                         ct_mms_info->len + ct_mms_info->padding, unicode_buffer,
+	                         strlen(buffer)*2 + ct_mms_info->padding + zero_padding);
+	DUMP_BYTES(unicode_buffer, 60);
+	
+	return 1;
+}
+
+static unsigned int
+mms_nat_expected(struct sk_buff **pskb,
+                 unsigned int hooknum,
+                 struct ip_conntrack *ct,
+                 struct ip_nat_info *info)
+{
+	struct ip_nat_multi_range mr;
+	u_int32_t newdstip, newsrcip, newip;
+
+	struct ip_conntrack *master = master_ct(ct);
+
+	IP_NF_ASSERT(info);
+	IP_NF_ASSERT(master);
+
+	IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
+
+	DEBUGP("ip_nat_mms: mms_nat_expected: We have a connection!\n");
+
+	newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+	newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+	DEBUGP("ip_nat_mms: mms_nat_expected: hook %s: newsrc->newdst %u.%u.%u.%u->%u.%u.%u.%u\n",
+	       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+	       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+	       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???",
+	       NIPQUAD(newsrcip), NIPQUAD(newdstip));
+
+	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
+		newip = newsrcip;
+	else
+		newip = newdstip;
+
+	DEBUGP("ip_nat_mms: mms_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
+
+	mr.rangesize = 1;
+	/* We don't want to manip the per-protocol, just the IPs. */
+	mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+	mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+	return ip_nat_setup_info(ct, &mr, hooknum);
+}
+
+
+static unsigned int mms_nat_help(struct ip_conntrack *ct,
+			 struct ip_conntrack_expect *exp,
+			 struct ip_nat_info *info,
+			 enum ip_conntrack_info ctinfo,
+			 unsigned int hooknum,
+			 struct sk_buff **pskb)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
+	unsigned int datalen;
+	int dir;
+	struct ip_ct_mms_expect *ct_mms_info;
+
+	if (!exp)
+		DEBUGP("ip_nat_mms: no exp!!");
+
+	ct_mms_info = &exp->help.exp_mms_info;
+	
+	/* Only mangle things once: original direction in POST_ROUTING
+	   and reply direction on PRE_ROUTING. */
+	dir = CTINFO2DIR(ctinfo);
+	if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
+	    ||(hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
+		DEBUGP("ip_nat_mms: mms_nat_help: not touching dir %s at hook %s\n",
+		       dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
+		       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+		       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+		       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
+		return NF_ACCEPT;
+	}
+	DEBUGP("ip_nat_mms: mms_nat_help: beyond not touching (dir %s at hook %s)\n",
+	       dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
+	       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+	       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+	       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
+	
+	datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
+	
+	DEBUGP("ip_nat_mms: mms_nat_help: %u+%u=%u %u %u\n", exp->seq, ct_mms_info->len,
+	       exp->seq + ct_mms_info->len,
+	       ntohl(tcph->seq),
+	       ntohl(tcph->seq) + datalen);
+	
+	LOCK_BH(&ip_mms_lock);
+	/* Check wether the whole IP/proto/port pattern is carried in the payload */
+	if (between(exp->seq + ct_mms_info->len,
+	    ntohl(tcph->seq),
+	    ntohl(tcph->seq) + datalen)) {
+		if (!mms_data_fixup(ct_mms_info, ct, pskb, ctinfo, exp)) {
+			UNLOCK_BH(&ip_mms_lock);
+			return NF_DROP;
+		}
+	} else {
+		/* Half a match?  This means a partial retransmisison.
+		   It's a cracker being funky. */
+		if (net_ratelimit()) {
+			printk("ip_nat_mms: partial packet %u/%u in %u/%u\n",
+			       exp->seq, ct_mms_info->len,
+			       ntohl(tcph->seq),
+			       ntohl(tcph->seq) + datalen);
+		}
+		UNLOCK_BH(&ip_mms_lock);
+		return NF_DROP;
+	}
+	UNLOCK_BH(&ip_mms_lock);
+	
+	return NF_ACCEPT;
+}
+
+static struct ip_nat_helper mms[MAX_PORTS];
+static char mms_names[MAX_PORTS][10];
+
+/* Not __exit: called from init() */
+static void fini(void)
+{
+	int i;
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		DEBUGP("ip_nat_mms: unregistering helper for port %d\n", ports[i]);
+		ip_nat_helper_unregister(&mms[i]);
+	}
+}
+
+static int __init init(void)
+{
+	int i, ret = 0;
+	char *tmpname;
+
+	if (ports[0] == 0)
+		ports[0] = MMS_PORT;
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+
+		memset(&mms[i], 0, sizeof(struct ip_nat_helper));
+
+		mms[i].tuple.dst.protonum = IPPROTO_TCP;
+		mms[i].tuple.src.u.tcp.port = htons(ports[i]);
+		mms[i].mask.dst.protonum = 0xFFFF;
+		mms[i].mask.src.u.tcp.port = 0xFFFF;
+		mms[i].help = mms_nat_help;
+		mms[i].me = THIS_MODULE;
+		mms[i].flags = 0;
+		mms[i].expect = mms_nat_expected;
+
+		tmpname = &mms_names[i][0];
+		if (ports[i] == MMS_PORT)
+			sprintf(tmpname, "mms");
+		else
+			sprintf(tmpname, "mms-%d", i);
+		mms[i].name = tmpname;
+
+		DEBUGP("ip_nat_mms: register helper for port %d\n",
+				ports[i]);
+		ret = ip_nat_helper_register(&mms[i]);
+
+		if (ret) {
+			printk("ip_nat_mms: error registering "
+			       "helper for port %d\n", ports[i]);
+			fini();
+			return ret;
+		}
+		ports_c++;
+	}
+
+	return ret;
+}
+
+module_init(init);
+module_exit(fini);
