diff -uPr linux-2.2.19-LEAF-orig/net/ipv4/Makefile linux/net/ipv4/Makefile
--- linux-2.2.19-LEAF-orig/net/ipv4/Makefile	Tue Jul 10 11:31:17 2001
+++ linux/net/ipv4/Makefile	Tue Jul 10 13:56:04 2001
@@ -118,6 +118,7 @@
 M_OBJS += ip_masq_user.o
 M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o
 M_OBJS += ip_masq_vdolive.o ip_masq_cuseeme.o
+M_OBJS += ip_masq_dplay.o ip_masq_h323.o ip_masq_icq.o ip_masq_mms.o
 endif
 
 ifeq ($(CONFIG_SYN_COOKIES),y)
diff -uPr linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_dplay.c linux/net/ipv4/ip_masq_dplay.c
--- linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_dplay.c	Wed Dec 31 18:00:00 1969
+++ linux/net/ipv4/ip_masq_dplay.c	Tue Jul 10 13:53:07 2001
@@ -0,0 +1,529 @@
+#define __KERNEL__
+#define MODULE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <net/ip.h>
+#include <linux/ip_fw.h>
+#include <linux/ip_masq.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+
+#define DPLAY_PORT_MIN 2300
+#define DPLAY_PORT_MAX 2400
+#define DPLAY_PORT_SRV 47624
+
+#define cl	((struct dplay_session *)ms->app_data)
+
+static struct ip_masq_app *dplay_app;
+
+struct dplay_session {
+    struct	list_head list;
+    __u32	client_addr, server_addr;
+    __u16	client_tcp_port, client_udp_port, server_tcp_port, server_udp_port;
+};
+
+static struct ip_masq_mod *mmod_self = NULL;
+
+static struct list_head session_list;
+static rwlock_t dplay_lock = RW_LOCK_UNLOCKED;
+
+static char *host = NULL;
+__u32 host_addr = 0;
+
+static __inline__ int dplay_session_del (__u16 protocol, __u16 client_port, __u32 client_addr)
+{
+	struct dplay_session *n;
+	struct list_head *entry;
+	struct list_head *list = &session_list;
+	__u16 port;
+	int nent;
+
+	if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP)
+		return 1;
+
+	if (client_port < DPLAY_PORT_MIN || client_port > DPLAY_PORT_MAX)
+		return 1;
+	
+	nent = atomic_read (&mmod_self->mmod_nent);
+
+	write_lock_bh (&dplay_lock);
+
+	// iterate through the list of entries
+	// well, I hope that's what it's doing
+	for (entry = list->next; entry != list; entry = entry->next) {
+		n = list_entry (entry, struct dplay_session, list);
+		// compare the passed port to the appropriate stored
+		// port based on the protocol given
+		if (protocol == IPPROTO_TCP) {
+			port = n->client_tcp_port;
+		} else {
+			port = n->client_udp_port;
+		} 
+		if (port == client_port &&
+			(!client_addr || n->client_addr == client_addr)) {
+			entry = n->list.prev;
+			list_del (&n->list);
+			ip_masq_mod_dec_nent (mmod_self);
+			kfree_s (n, sizeof (struct dplay_session));
+			MOD_DEC_USE_COUNT;
+		}
+	}
+
+	write_unlock_bh (&dplay_lock);
+
+	return nent==atomic_read (&mmod_self->mmod_nent) ? ESRCH : 0;
+}
+
+static __inline__ void dplay_session_flush (void)
+{
+	struct dplay_session *n;
+	struct list_head *l, *e;
+
+	write_lock_bh (&dplay_lock);
+
+	l = &session_list;
+	while ((e = l->next) != l) {
+		ip_masq_mod_dec_nent (mmod_self);
+		n = list_entry (e, struct dplay_session, list);
+		list_del (e);
+		kfree_s (n, sizeof (*n));
+		MOD_DEC_USE_COUNT;
+	}
+
+	write_unlock_bh (&dplay_lock);
+}
+
+static __inline__ struct dplay_session * dplay_session_lookup (__u16 protocol, __u16 client_port, __u32 client_addr)
+{
+	struct dplay_session *n = NULL;
+	struct list_head *l, *e;
+	__u16 port;
+
+	if ((client_port < DPLAY_PORT_MIN || client_port > DPLAY_PORT_MAX) && client_port != 0)
+		return NULL;
+	
+	l = &session_list;
+
+	for (e = l->next; e != l; e = e->next) {
+		n = list_entry (e, struct dplay_session, list);
+		if (protocol == IPPROTO_TCP) {
+			port = n->client_tcp_port;
+		} else if (protocol == IPPROTO_UDP) {
+			port = n->client_udp_port;
+		} else {
+			return NULL;
+		}
+		if ((!client_port || port == client_port) && (!client_addr || n->client_addr == client_addr))
+			return n;
+	}
+
+	return NULL;
+}
+
+static __inline__ int dplay_session_add (__u16 protocol, __u16 client_port, __u32 client_addr)
+{
+	struct dplay_session *npf;
+	
+    if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP)
+		return 1;
+
+	if (client_port < DPLAY_PORT_MIN || client_port > DPLAY_PORT_MAX)
+		return 1;
+	
+	npf = (struct dplay_session*) kmalloc (sizeof (struct dplay_session), GFP_KERNEL);
+
+	if (!npf)
+		return ENOMEM;
+
+	MOD_INC_USE_COUNT;
+	memset (npf, 0, sizeof (*npf));
+
+	npf->client_tcp_port = 0;
+	npf->client_udp_port = 0;
+	npf->server_tcp_port = 0;
+	npf->server_udp_port = 0;
+	npf->client_addr = client_addr;
+	npf->server_addr = 0;
+
+	if (protocol == IPPROTO_UDP) {
+		npf->client_udp_port = client_port;
+	} else {
+		npf->client_tcp_port = client_port;
+	}
+
+	INIT_LIST_HEAD (&npf->list);
+
+	write_lock_bh (&dplay_lock);
+
+	list_add (&npf->list, &session_list);
+
+	write_unlock_bh (&dplay_lock);
+
+	ip_masq_mod_inc_nent (mmod_self);
+	return 0;
+}
+	
+#ifdef CONFIG_PROC_FS
+
+int dplay_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+
+static struct proc_dir_entry proc_masq_dplay = {
+	0, 5, "dplay",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	dplay_get_info
+};
+
+#define proc_ent &proc_masq_dplay
+#else
+#define proc_ent NULL
+#endif
+
+static int masq_dplay_start (struct ip_masq_app *mapp, struct ip_masq *ms);
+static int masq_dplay_fin (struct ip_masq_app *mapp, struct ip_masq *ms);
+static int masq_dplay_pkt_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr);
+static int masq_dplay_pkt_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr);
+
+static struct ip_masq_app ip_masq_dplay = {
+	NULL,				// next				//
+	"dplay",			// name				// 
+	0,					// type				// assigned automatically by register_ip_masq_app
+	0,					// n_attach			// assigned automatically by register_ip_masq_app
+	masq_dplay_start,	// ip_masq_init_1	//
+	masq_dplay_fin,		// ip_masq_done_1	//
+	masq_dplay_pkt_out,	// pkt_out			//
+	masq_dplay_pkt_in	// pkt_in			//
+};
+
+static int masq_dplay_start (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	// initialize the ms->app_data field, since otherwise
+	// masquerading does not seem to take place?
+	if (!(ms->app_data = kmalloc (sizeof (struct dplay_session), GFP_ATOMIC)))
+		return -ENOMEM;
+
+	cl->client_tcp_port = 0;
+	cl->client_udp_port = 0;
+	cl->client_addr = 0;
+	cl->server_tcp_port = 0;
+	cl->server_udp_port = 0;
+	cl->server_addr = 0;
+
+	return 0;
+}
+	
+static int masq_dplay_fin (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	// we don't actually want to do this, but oh well...	
+	if (cl->client_tcp_port) {
+		dplay_session_del (IPPROTO_TCP, cl->client_tcp_port, cl->client_addr);
+		printk (KERN_INFO "ip_masq_dplay: Session between %u.%u.%u.%u and %u.%u.%u.%u has expired.\n", NIPQUAD (cl->client_addr), NIPQUAD (cl->server_addr));
+	}	
+	kfree (ms->app_data);
+	ms->app_data = NULL;
+		
+	return 0;
+}
+
+static int masq_dplay_pkt_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+	printk (KERN_INFO "ip_masq_dplay: is anything happening anywhere?\n");
+
+	if (ms->dport == DPLAY_PORT_SRV || htons (ms->dport) == DPLAY_PORT_SRV) {
+		printk (KERN_INFO "ip_masq_dplay: hello jello!\n");
+	}
+
+	return 0;
+}
+
+static int masq_dplay_pkt_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+	struct sk_buff *skb = *skb_p;
+	struct iphdr *iph = skb->nh.iph;
+	__u8 *data = (__u8 *)((struct udphdr *)&(((__u8 *)iph)[iph->ihl << 2])+1);
+	unsigned len = skb->len - (data - skb->h.raw);
+	struct dplay_session *ds;
+
+	if (iph->protocol != IPPROTO_UDP)
+		return 0;
+
+	if (len != 52)
+		return 0;
+
+	cl->client_tcp_port = (*(data + 6) << 8) | *(data + 7);
+	if (cl->client_tcp_port < DPLAY_PORT_MIN || cl->client_tcp_port > DPLAY_PORT_MAX)
+		return 0;
+	
+	cl->server_tcp_port = htons (ms->dport);
+	cl->client_addr = ms->saddr;
+	cl->server_addr = ms->daddr;
+
+	dplay_session_add (IPPROTO_TCP, cl->client_tcp_port, cl->client_addr);
+
+	ds = dplay_session_lookup (IPPROTO_TCP, cl->client_tcp_port, cl->client_addr);
+	if (!ds) {
+		printk (KERN_WARNING "ip_masq_dplay: DirectPlay session could not be tracked!\n");
+		return 0;
+	}
+	ds->server_addr = cl->server_addr;
+	ds->server_tcp_port = cl->server_tcp_port;
+
+	printk (KERN_INFO "ip_masq_dplay: %u.%u.%u.%u is connecting to %u.%u.%u.%u.\nip_masq_dplay: --- Client TCP Port: %u.\n", NIPQUAD (cl->client_addr), NIPQUAD (cl->server_addr), cl->client_tcp_port);
+
+	return 0;
+}
+
+static int dplay_in_rule (const struct sk_buff *skb, const struct iphdr *iph);
+static struct ip_masq * dplay_in_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr);
+static int dplay_out_rule (const struct sk_buff *skb, const struct iphdr *iph);
+static int dplay_out_update (const struct sk_buff *skb, const struct iphdr *iph, struct ip_masq *ms);
+static struct ip_masq * dplay_out_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr);
+
+static struct ip_masq_mod dplay_mod = {
+	NULL,				// next				//
+	NULL,				// next_reg			//
+	"dplay",			// name				//
+	ATOMIC_INIT(0),		// nent				//
+	ATOMIC_INIT(0),		// refcnt			//
+	proc_ent,			
+	NULL,
+	NULL,				// masq_mod_init	//
+	NULL,				// masq_mod_done	//
+	dplay_in_rule,
+	NULL,
+	dplay_in_create,
+	dplay_out_rule,
+	dplay_out_update,
+	dplay_out_create
+};
+
+static int dplay_in_rule (const struct sk_buff *skb, const struct iphdr *iph)
+{
+	const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+	struct dplay_session *ds;
+	int ret = IP_MASQ_MOD_NOP;
+	
+	read_lock (&dplay_lock);
+	ds = dplay_session_lookup(iph->protocol, ntohs (portp[1]), 0);
+	read_unlock (&dplay_lock);
+
+	if (ds)
+		ret = IP_MASQ_MOD_ACCEPT;
+	
+	if (host_addr && (ntohs (portp[1]) == DPLAY_PORT_SRV)) {
+		printk (KERN_INFO "ip_masq_dplay: Connection request from %u.%u.%u.%u being forwarded to %u.%u.%u.%u.  %u\n", NIPQUAD(iph->saddr), NIPQUAD(host_addr), iph->tos);
+		ret = IP_MASQ_MOD_ACCEPT;
+	}
+	
+	return ret;
+}
+
+static struct ip_masq * dplay_in_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+	const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+	__u16 port;
+	struct ip_masq *ms = NULL;
+	struct dplay_session *ds;
+
+	write_lock (&dplay_lock);
+
+	if (ntohs (portp[1]) == DPLAY_PORT_SRV) {
+		// somehow we need to get the udp/tcp ports the server is sending back to the client, so we can add
+		// these ports to our session list...
+		ms = ip_masq_new (iph->protocol, iph->daddr, portp[1], host_addr, portp[1], iph->saddr, portp[0], 0);
+			// this should forward our packet to the local server
+		ip_masq_listen (ms);
+	} else if ((ds = dplay_session_lookup (iph->protocol, ntohs (portp[1]), 0))) {
+		if (iph->protocol == IPPROTO_TCP)
+			port = htons (ds->client_tcp_port);
+		else
+			port = htons (ds->client_udp_port);
+		ms = ip_masq_new (iph->protocol, iph->daddr, portp[1], ds->client_addr, port, iph->saddr, portp[0], 0);
+		ip_masq_listen (ms);
+		}
+	write_unlock (&dplay_lock);
+	return ms;
+}
+
+static int dplay_out_rule (const struct sk_buff *skb, const struct iphdr *iph)
+{
+	return IP_MASQ_MOD_NOP;
+}
+
+static struct ip_masq * dplay_out_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+	return NULL;
+}
+
+static int dplay_out_update (const struct sk_buff *skb, const struct iphdr *iph, struct ip_masq *ms)
+{
+	__u8 *data = (__u8 *)((struct tcphdr *)&(((__u8 *)iph)[iph->ihl << 2])+1);
+	unsigned len = skb->len - (data - skb->h.raw);
+	struct dplay_session *ds;
+	__u16 port;
+
+	if (iph->protocol != IPPROTO_TCP)
+		return 0;
+	
+	if ((len == 128) && (*(data + 4) == 0x2)) {
+		port = ((*(data + 6)) << 8) | (*(data + 7));
+		ds = dplay_session_lookup (iph->protocol, port, 0);
+		if (!ds) {
+			dplay_session_add (iph->protocol, port, iph->saddr);
+			ip_masq_mod_dec_nent (&dplay_mod);
+			MOD_DEC_USE_COUNT;
+		}
+		return 0;
+	}
+
+	read_lock (&dplay_lock);
+	ds = dplay_session_lookup (iph->protocol, 0, iph->saddr);
+	read_unlock (&dplay_lock);
+
+	if (!ds)
+		return 0;
+
+	if ((len == 134) && (*(data + 112) == 0x2) && (*(data + 113) == 0x0)) {
+		port = ((*(data + 114)) << 8) | (*(data + 115));
+
+		if (port < DPLAY_PORT_MIN || port > DPLAY_PORT_MAX)
+			return 0;
+
+		ds->client_udp_port = port;
+	
+		printk (KERN_INFO "ip_masq_dplay: --- Client UDP Port: %u.\n", ds->client_udp_port);
+	
+		return 0;
+	} else if ((len == 339) && (*(data + 242) == 0x2) && (*(data + 243) == 0x0)) {
+		port = ((*(data + 244)) << 8) | (*(data + 245));
+
+		if (port < DPLAY_PORT_MIN || port > DPLAY_PORT_MAX)
+			return 0;
+
+		ds->client_udp_port = port;
+
+		return 0;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int dplay_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	struct dplay_session *n = NULL;
+	struct list_head *l, *e;
+
+	if (offset > 0)
+	  return 0;
+
+	l = &session_list;
+
+	if (!host_addr) {
+		len = sprintf(buffer, "Host address: <none>\n");
+	} else {
+		len = sprintf(buffer, "Host address: %u.%u.%u.%u\n", NIPQUAD (host_addr));
+	}
+
+  	len += sprintf(buffer+len, "Masquerading DirectPlay connections:\n");
+
+	for (e = l->next; e != l; e = e->next) {
+		n = list_entry (e, struct dplay_session, list);
+		len += sprintf(buffer+len, "client: %u.%u.%u.%u <---> server: %u.%u.%u.%u\n", NIPQUAD (n->client_addr),  NIPQUAD (n->server_addr));
+	}
+	
+	*start = buffer;
+
+	return len;
+}
+
+#endif
+
+__initfunc(int dplay_init(void))
+{
+    int ret, i, back = 0, n = 0;
+	__u8 q[4] = {0,0,0,0};
+	char temp[16];
+    INIT_LIST_HEAD(&session_list);
+
+    ret = register_ip_masq_mod ((mmod_self=&dplay_mod));
+    if (!ret) {
+		if ((host) && (strlen (host) < 16)) {
+			for (i = 0; i < strlen (host) + 1; i++) {
+				if ((host[i] == '.' || host[i] == '\0') && (n < 4)) {
+					if (i - back > 3)
+						break;
+					strncpy(temp, (host + back), i - back);
+					temp[i - back] = '\0';
+					q[n] = (__u8) simple_strtoul (temp, NULL, 10);
+					back = i + 1;
+					n++;
+				}
+			}
+			if (n != 4)
+				q[0] = q[1] = q[2] = q[3] = 0;
+		}
+		host_addr = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
+		if (host_addr)
+			ip_masq_mod_inc_nent (&dplay_mod); // increases our entry(?) count to hear incoming server requests.
+		dplay_app = kmalloc (sizeof (struct ip_masq_app), GFP_KERNEL);
+		memcpy (dplay_app, &ip_masq_dplay, sizeof (struct ip_masq_app));
+		ret = register_ip_masq_app (dplay_app, IPPROTO_UDP, DPLAY_PORT_SRV);
+		if (!ret) {
+			printk (KERN_INFO "ip_masq_dplay: Listening to outbound connections on port %u.\n", DPLAY_PORT_SRV);
+			if (host_addr)
+				printk (KERN_INFO "ip_masq_dplay: Inbound connections will be forwarded to %u.%u.%u.%u.\n", NIPQUAD (host_addr));
+		}
+	}
+
+	return ret;
+}
+
+int dplay_done(void)
+{
+	if (host_addr)
+		ip_masq_mod_dec_nent (&dplay_mod); // decreases our entry(?) count.
+	unregister_ip_masq_app (dplay_app);
+    kfree (dplay_app);
+
+	return unregister_ip_masq_mod (&dplay_mod);
+}
+	
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_DESCRIPTION ("DirectPlay Masquerading Module");
+MODULE_AUTHOR ("nezroy");
+MODULE_PARM (host, "0-1s");
+
+int init_module(void)
+{
+	printk (KERN_INFO "ip_masq_dplay: Starting DirectPlay(R) masquerade module.\n");
+
+    if (dplay_init() != 0) {
+		printk (KERN_WARNING "ip_masq_dplay: Module did not start successfully!\n");
+    	return -EIO;
+	}
+	
+	return 0;
+}
+
+void cleanup_module(void)
+{
+    if (dplay_done() != 0)
+        printk (KERN_WARNING "ip_masq_dplay: Can't remove module!\n");
+	else
+		printk (KERN_INFO "ip_masq_dplay: Stopping DirectPlay(R) masquerade module.\n");
+}
+
+#endif // MODULE //
+
+/* vim:set ts=4: */
diff -uPr linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_h323.c linux/net/ipv4/ip_masq_h323.c
--- linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_h323.c	Wed Dec 31 18:00:00 1969
+++ linux/net/ipv4/ip_masq_h323.c	Tue Jul 10 13:52:10 2001
@@ -0,0 +1,515 @@
+/*
+ *              IP_MASQ_H323, H.323 masquerading module
+ *
+ *
+ * Version:     2.2.0 - 16 October 2000
+ *
+ * Author:      Rajkumar. S
+ *              Archana V. S.
+ *              Sheenarani I.
+ *
+ *              ____Released by CoRiTeL (www.coritel.it)___
+ *              Luca Veltri
+ *              Stefano Giacometti
+ *              Raffaele Pellicciotta
+ *              Paolo Iurilli
+ *
+ *              to work on kernel 2.2.12 and NetMeeting 3.01
+ *
+ *              official site: http://www.coritel.it/projects/sofia/nat.html
+ *
+ *      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.
+ *
+ * Fixes:
+ * 	 Juan Jose Ciarlante:	coding cleanups
+ *   Paolo Iurilli,Luca Veltri:
+ *	 CORITEL 04.09.00: ip_masq_h225_app and ip_masq_h245_app dynamic allocation cleanups
+ *   Paolo Iurilli: 
+ *   CORITEL: incoming calls code integration and ip_masq_timeout_table introduction  
+ *   
+ */
+
+
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+
+#define IP_MASQ_H323_DEBUG(msg...) (1-debug<=ip_masq_get_debug_level())? printk(KERN_DEBUG "IP_MASQ:" ## msg) : 0
+
+static int debug=0;
+MODULE_PARM(debug, "i");
+
+/*
+ *  	port_h225 is set to the default h.225 port (1720)
+ */
+int port_h225 = 1720;
+
+/*  CORITEL 29/09/2000 : private h323 timeout table */ 
+struct ip_masq_timeout_table h323_timeout_table = {
+	ATOMIC_INIT(0),		/* refcnt */
+	0,			/* scale  */
+	{
+		30*60*HZ,	    /*      IP_MASQ_S_NONE,       */
+		15*60*HZ,	    /*      IP_MASQ_S_ESTABLISHED */
+		10*60*HZ,		/*      IP_MASQ_S_SYN_SENT,   */
+		10*60*HZ,		/*      IP_MASQ_S_SYN_RECV,   */
+		1*60*HZ,	    /*      IP_MASQ_S_FIN_WAIT,   */
+		10*60*HZ,		/*      IP_MASQ_S_TIME_WAIT,  */
+		5*60*HZ,		/*      IP_MASQ_S_CLOSE,      */
+		5*60*HZ,		/*      IP_MASQ_S_CLOSE_WAIT, */
+		10*60*HZ,		/*      IP_MASQ_S_LAST_ACK,   */
+		2*60*HZ,	    /*      IP_MASQ_S_LISTEN,     */
+		10*60*HZ,	    /*      IP_MASQ_S_UDP,        */
+		10*60*HZ,		/*      IP_MASQ_S_ICMP,       */
+		2*HZ,		    /*      IP_MASQ_S_LAST        */
+	},			/* timeout */
+};
+
+/*@@@@@ private comments (CoRiTeL)...
+ *  some notes:
+ *  For every call from private host:
+ *  1) every h.225 session regists a new 'h245 application' to handle
+ *  with a new h.245 session, this 'h245 appl.' is always registered with
+ *  the same ip_masq_h245 structure; for this reason only
+ *  one h245 session can be handled in the same time;
+ *  2) for this reason,
+ *  when a new h245 session is needed, the old one is deregistrated and the
+ *  'attach' value is decreased of one;
+ *  For every call from public host:
+ *  1) a new ip_masq entry is made for H.245 tcp connection
+ *  2) a new h245 application is binded to the ip_masq entry made without
+ *  registration
+ */
+
+
+
+/* CORITEL 05.09.00:
+ * Move an ip_masq_app from an old port to a new port
+ */
+int ip_masq_move_app(struct ip_masq_app* mapp, unsigned short protocol, int new_port)
+{	int n_attach_saved=mapp->n_attach;
+	mapp->n_attach=0;
+	if(unregister_ip_masq_app(mapp))
+	{	/* deregistration failed */
+		mapp->n_attach=n_attach_saved;
+		return -2;
+	} else
+		if (register_ip_masq_app(mapp, protocol, new_port))
+		{	/* registration failed */
+			mapp->n_attach=n_attach_saved;
+			return -1;
+		} else
+			{	mapp->n_attach=n_attach_saved;
+				return 0;
+			}
+}
+
+/*	CORITEL 11.09.00
+ *	unbind ip_masq
+ */
+
+int ip_masq_unbind_app(struct ip_masq *ms)
+ {
+         struct ip_masq_app * mapp;
+         mapp = ms->app;
+
+         if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
+                 return 0;
+
+         if (mapp != NULL) {
+                 if (mapp->masq_done_1) mapp->masq_done_1(mapp, ms);
+                 ms->app = NULL;
+                 mapp->n_attach--;
+         }
+         return (mapp != NULL);
+ }
+/*	CORITEL 08.09.00
+ *	Bind ip_masq to a specific ip_masq_app, only for incoming calls
+ */
+
+struct ip_masq_app* ip_masq_bind_this_app(struct ip_masq* ms, struct ip_masq_app* mapp)
+{
+	if (mapp == NULL) return NULL;
+
+	/* allow binding if already bound */
+	if (ms->app != NULL)
+	{	IP_MASQ_H323_DEBUG("ip_masq_bind_this_app() called for already bound object: old app REPLACED!\n");
+        ip_masq_unbind_app(ms);
+	}
+	else IP_MASQ_H323_DEBUG("ip_masq_bind_this_app() called for not yet bound object!\n");
+	ms->app = mapp;
+        if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
+    mapp->n_attach++;
+	return mapp;
+}
+
+int h245registered=0;
+
+struct ip_masq_app * masq_h245_app_debug;
+
+
+/**********  Start of H.245 masq app functions ******************/
+
+static int masq_h245_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	/* setup the control connection with a new timeout table */
+	if (!ms->timeout_table){
+		          ms->timeout_table = &h323_timeout_table;
+                  IP_MASQ_H323_DEBUG("masq_h245_init_1: ## NEW TIMEOUT TABLE ##\n");
+			   }
+	IP_MASQ_H323_DEBUG("masq_h245_init_1: IP masq app 245 init\n");
+    MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int masq_h245_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	MOD_DEC_USE_COUNT;
+	IP_MASQ_H323_DEBUG("masq_h245_done_1: IP masq app 245 done\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+	return 0;
+}
+
+
+int masq_h245_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+	IP_MASQ_H323_DEBUG("masq_h245_in\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+
+	return 0;
+}
+
+int masq_h245_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct tcphdr *th;
+	unsigned char *data, *data_limit;
+	unsigned char *skbuff_p;
+	__u16 data_port, *rtp_port;
+	__u32 data_ip, *rtp_ip;
+	struct ip_masq * n_ms_rtp;
+
+	skb = *skb_p;
+	iph = skb->h.ipiph;
+	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+	data = skb->data;
+	data_limit = skb->tail;
+	skbuff_p = data+12;
+
+	IP_MASQ_H323_DEBUG("masq_h245_out\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+
+	/*@@@@@ CoRiTeL: analisys of h245 msg */
+	data=skb->data; 		/*@@@@@ CoRiTeL: start of IP pkt */
+	data+=iph->ihl*4+th->doff*4;	/*@@@@@ CoRiTeL: jumps to app data */
+	while((data+5)<data_limit) {
+		data_ip=*((__u32*)data);
+		if (data_ip==ms->saddr)
+		{
+			data_port=*((__u16*)(data+4));
+			IP_MASQ_H323_DEBUG("masq_h245_out: <-- RTCP/RTP_dest: %d.%d.%d.%d:%d\n",NIPQUAD(data_ip), data_port);
+
+			n_ms_rtp = ip_masq_new(
+				IPPROTO_UDP, maddr,0,	/*@@@@@ CoRiTeL:  masq addr */
+				data_ip, data_port,	/*@@@@@ CoRiTeL:  pck source */
+				iph->daddr, 0,		/*@@@@@ CoRiTeL:  pck dest */
+				IP_MASQ_F_NO_DPORT);
+			if(n_ms_rtp==NULL)
+				IP_MASQ_H323_DEBUG("masq_h245_out: RTCP/RTP masq entry not made\n");
+			else {
+                               ip_masq_control_add(n_ms_rtp,ms); 
+                               ip_masq_listen(n_ms_rtp);
+			       ip_masq_put(n_ms_rtp);
+                                /* CORITEL 02/10/2000 */
+			        /* setup the control connection with a new timeout table */
+	                       if (!n_ms_rtp->timeout_table){
+		                          n_ms_rtp->timeout_table = &h323_timeout_table;
+		                          IP_MASQ_H323_DEBUG("masq_h245_out :## setup NEW TIMEOUT TABLE\n ##");
+					  }	
+				rtp_ip=(__u32*)data;
+				rtp_port=(__u16*)(data+4);
+				/*	Hack the packet  */
+				*rtp_ip=n_ms_rtp->maddr;
+				*rtp_port=n_ms_rtp->mport;
+				IP_MASQ_H323_DEBUG("masq_h245_out: RTCP/RTP masquerated: maddr=%d.%d.%d.%d , mport=%u\n", NIPQUAD(n_ms_rtp->maddr),ntohs(n_ms_rtp->mport));
+				}
+
+		}
+
+		data++;
+	}
+
+	return 0;
+}
+
+
+/***********End of H.245 app functions *******************/
+
+struct ip_masq_app  ip_masq_h245_app = {
+	NULL,			/* next */
+	"h245",			/* name */
+	0,                      /* type */
+	0,                      /* n_attach */
+	masq_h245_init_1,        /* ip_masq_init_1 */
+	masq_h245_done_1,        /* ip_masq_done_1 */
+	masq_h245_out,           /* pkt_out */
+	masq_h245_in,            /* pkt_in */
+};
+
+
+static int masq_h225_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{	IP_MASQ_H323_DEBUG("masq_h225_init_1\n");
+    /* setup the control connection with a new timeout table */
+	if (!ms->timeout_table){
+		      ms->timeout_table = &h323_timeout_table;
+              IP_MASQ_H323_DEBUG("masq_h225_init_1: ## NEW TIMEOUT TABLE ##\n");
+			  }
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int masq_h225_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+	IP_MASQ_H323_DEBUG("masq_h225_done_1\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+	IP_MASQ_H323_DEBUG("masq_h225: h245 status (0=unregistered, 1=registered) = %d\n",h245registered); /*@@@@@ CoRiTeL: debug*/
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int masq_h225_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{   /* CORITEL 08.09.00: */
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct tcphdr *th;
+	unsigned char *data, *data_limit;
+	unsigned char *skbuff_p;
+	__u16 data_port, *h245_port;
+	__u32 data_ip, *h245_ip;
+	struct ip_masq *n_ms;
+	int f_alarm; /*CORITEL 06/10/2000: patch for voxilla, unnecessary for netmeeting and perhaps other application, 
+	                  voxilla makes some false alarm */
+	IP_MASQ_H323_DEBUG("masq_h225_out:\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+
+		skb = *skb_p;
+		iph = skb->h.ipiph;
+		th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+		data = skb->data;
+		data_limit = skb->tail;
+		skbuff_p = data+12;
+	    /* CORITEL: analisys of h225 msg */
+		data=skb->data;
+		data+=iph->ihl*4+th->doff*4;
+                f_alarm=0;
+		while((data+5)<data_limit) {
+			data_ip=*((__u32*)data);
+			if (data_ip==ms->saddr)
+			{
+				data_port=*((__u16*)(data+4));
+				/* CORITEL: incoming call from private host: substitution addr and port field in payload msg only */  
+				if(data_port==ms->sport)
+					{	h245_ip=(__u32*)data;
+						h245_port=(__u16*)(data+4);
+                        /*	Hack the packet  */
+				      	*h245_ip=ms->maddr;
+					    *h245_port=ms->mport;
+					     f_alarm=1;/* patch for voxilla */
+                                             IP_MASQ_H323_DEBUG("masq_h225_out: call from private host substitution addr and port field/n"); 
+                    }
+				else if(!f_alarm)    /* CORITEL: incoming call from public host: new ip_masq entry  */  
+				{	   IP_MASQ_H323_DEBUG("masq_h225_out: private host:port <--  %d.%d.%d.%d:%d\n",NIPQUAD(data_ip), data_port);
+				       IP_MASQ_H323_DEBUG("masq_h225_out:It means that the call is incoming from public host!\n");
+		               
+					   n_ms = ip_masq_new(
+	                                   IPPROTO_TCP, maddr,0,
+					   data_ip, data_port,
+					   iph->daddr, 0,
+					   IP_MASQ_F_NO_DPORT);
+				       if(n_ms==NULL)
+				            	IP_MASQ_H323_DEBUG("masq_h225_out: h245 ip_masq entry not made\n");
+				       else {    ip_masq_control_add(n_ms,ms); 
+                                                 ip_masq_listen(n_ms);
+			 			 ip_masq_put(n_ms);
+						 /* CORITEL 02/10/2000 */
+						 /* setup the control connection with a new timeout table */
+	                                         if (!n_ms->timeout_table){
+		                                         n_ms->timeout_table = &h323_timeout_table;
+				                         IP_MASQ_H323_DEBUG("masq_h225_out : ## NEW TIMEOUT TABLE ##\n");	
+				                  }
+						  h245_ip=(__u32*)data;
+					             h245_port=(__u16*)(data+4);
+					             /*	Hack the packet  */
+					             *h245_ip=n_ms->maddr;
+					             *h245_port=n_ms->mport;
+				                 IP_MASQ_H323_DEBUG("masq_h225_out: masquerated: maddr=%d.%d.%d.%d , mport=%u\n", NIPQUAD(n_ms->maddr),ntohs(n_ms->mport));
+				                 ip_masq_bind_this_app(n_ms,&ip_masq_h245_app); /* no registration of ip_masq_h245_app */
+				             }       
+                }  
+			}
+			data++;
+		}
+
+	return 0;
+}
+
+int masq_h225_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct tcphdr *th;
+	unsigned char *data, *data_limit;
+	__u32 source_ip,m_ip;
+	__u16 port;
+	__u32 temp_ip,*h225_ip;
+	unsigned char *skbuff_p;
+	unsigned char p1,p2;
+	int n_attach_saved;
+	int result;
+
+	IP_MASQ_H323_DEBUG("masq_h225_in\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+
+	skb = *skb_p;
+	iph = skb->h.ipiph;
+	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+	data = skb->data;
+	data_limit = skb->tail;
+	skbuff_p = data+12;
+	source_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
+	skbuff_p=skbuff_p+4;
+	temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
+	
+	while(((skbuff_p+6)< data_limit) && (temp_ip !=source_ip)){
+		skbuff_p++;
+		temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
+	}
+	if(temp_ip==source_ip){
+		p1=*(skbuff_p+4);
+		p2=*(skbuff_p+5);
+		port=(p1<<8)+p2;
+
+		IP_MASQ_H323_DEBUG("masq_h225_in: H.245 Port is at: %u\n",port);
+
+		/* CORITEL 05.09.00: bind the h245_app on the new port:
+		   - first, deregister the old ip_masq_h245_app,
+		   - then, register it on the new port.
+        */
+		if (h245registered)
+		{	IP_MASQ_H323_DEBUG("masq_h225_in: found an old ip_masq_app entry for h245, I'm try to deregist it and regist the new port...\n");
+		        result=ip_masq_move_app(&ip_masq_h245_app,IPPROTO_TCP,port);
+			switch (result)
+			{	case -2 : IP_MASQ_H323_DEBUG("masq_h225_in: deregistration failed\n"); break;
+				case -1 : IP_MASQ_H323_DEBUG("masq_h225_in: deregistration OK, registration failed\n"); h245registered--; break;
+				case 0 : IP_MASQ_H323_DEBUG("masq_h225_in: registration OK\n");
+			}
+		}
+		else
+			{	n_attach_saved=ip_masq_h245_app.n_attach;
+				if (register_ip_masq_app(&ip_masq_h245_app, IPPROTO_TCP, port)==0)
+				{	/* registration OK */
+					h245registered=1;
+					IP_MASQ_H323_DEBUG("masq_h225_in: registration of new h245_app OK\n");
+				} else
+					IP_MASQ_H323_DEBUG("masq_h225_in: registration of new h.245 _app failed\n");
+				ip_masq_h245_app.n_attach=n_attach_saved;
+			}
+	}
+	/* CORITEL 26/09/2000 */
+	/* CORITEL: incoming call from public host: substitution  */  
+	skbuff_p=data+16;
+	m_ip=ntohl(ms->maddr);
+	while(((skbuff_p+6)< data_limit) && (temp_ip!=m_ip)){
+	        skbuff_p++;
+		    temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
+	}
+	if (temp_ip==m_ip)
+	    {   /*	Hack the packet  */ 
+		    h225_ip=(__u32*)skbuff_p;
+			*h225_ip=ms->saddr;
+		}
+	return 0;
+}
+
+struct ip_masq_app ip_masq_h225_app = {
+	NULL,			/* next */
+	"h225",			/* name */
+	0,                      /* type */
+	0,                      /* n_attach */
+	masq_h225_init_1,        /* ip_masq_init_1 */
+	masq_h225_done_1,        /* ip_masq_done_1 */
+	masq_h225_out,           /* pkt_out */
+	masq_h225_in,            /* pkt_in */
+};
+
+/*
+ * 	ip_masq_h225 initialization
+ */
+
+int ip_masq_h225_init(void)
+{
+	IP_MASQ_H323_DEBUG("ip_masq_h225_init\n"); 
+	if (register_ip_masq_app(&ip_masq_h225_app, IPPROTO_TCP, port_h225))
+		IP_MASQ_H323_DEBUG("ip_masq_h225_init: H323: ERROR loading h225 support on port = %d\n", port_h225);
+		else IP_MASQ_H323_DEBUG("ip_masq_h225_init: H323: loaded h225 support on port = %d\n", port_h225);
+	return 0;
+}
+
+/*
+ * 	ip_masq_h225 fin.
+ */
+
+int ip_masq_h225_done(void)
+{	int n_attach_saved;
+
+	IP_MASQ_H323_DEBUG("ip_masq_h225_done\n");
+	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
+
+	if (unregister_ip_masq_app(&ip_masq_h225_app))
+		IP_MASQ_H323_DEBUG("ip_masq_h225_done: H323: ERROR unloading support on port = %d\n", port_h225);
+		else IP_MASQ_H323_DEBUG("ip_masq_h225_done: H323: unloaded support on port = %d\n", port_h225);
+
+	if (h245registered) {
+		IP_MASQ_H323_DEBUG("ip_masq_h225_done: removing the masq app entry\n");
+		n_attach_saved=ip_masq_h245_app.n_attach;
+		unregister_ip_masq_app(&ip_masq_h245_app);
+		ip_masq_h245_app.n_attach=n_attach_saved;
+		h245registered=0;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	IP_MASQ_H323_DEBUG("Init module\n");
+
+	/* CORITEL: DEBUG: */
+	masq_h245_app_debug=&ip_masq_h245_app;
+
+	if (ip_masq_h225_init() != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	if (ip_masq_h225_done() != 0)
+		IP_MASQ_H323_DEBUG("ip_masq_h225: can't remove module\n");
+}
+
+#endif /* MODULE */
diff -uPr linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_icq.c linux/net/ipv4/ip_masq_icq.c
--- linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_icq.c	Wed Dec 31 18:00:00 1969
+++ linux/net/ipv4/ip_masq_icq.c	Tue Jul 10 13:53:43 2001
@@ -0,0 +1,1165 @@
+/*
+ *              IP_MASQ_ICQ icq masquerading module
+ *
+ *
+ * Version:     @(#)ip_masq_icq.c 0.56	01 May 2000
+ *
+ * Author:      andrew deryabin <djsf@iname.com>
+ * Homepage:	http://members.xoom.com/djsf/masq-icq/
+ *
+ * Fixes:
+ *	Brandon Beretta	 : Proper loglevels for printk calls
+ *	Alan Cox	 : GFP_ATOMIC priority for kmallocs that are in an interrupt
+ *	Maurice Nonnekes : Fixed some warnings
+ *	El Cabazorro	 : Fixed improper `rest' calculation in `v5crypt'
+ *
+ *	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.
+ *
+ *	ICQ protocol specs taken from icq-devel mailing list
+ *	Thanx to Magnus Ihse and Sebastien Dault
+ *
+ *	To participate in the icq-devel mailing list, send a mail to
+ *	majordomo@lists.realminfo.com, with the message body consisting
+ *	only of the line "subscribe icq-devel".
+ *
+ *	To participate in the ip_masq_icq mailing list, send a mail to
+ *	majordomo@access.ru, with the message body consisting
+ *	only of the line "subscribe ip_masq_icq".
+ *
+ *	Best viewed with VIM	http://www.vim.org/
+ *
+ */
+
+#define __KERNEL__
+#define MODULE
+
+#include <linux/config.h>
+#ifdef CONFIG_MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+
+#define cl		((struct client *)ms->app_data)
+#define tcpdir		((struct tcp_direct *)ms->app_data)
+
+#define	icq_start_port	range[0]
+#define	icq_end_port	range[1]
+
+#define	LOG_LOGOUT	   1
+#define	LOG_LOGIN	   2
+#define	LOG_FWD		   4
+#define LOG_FWD_OUT	   8
+#define	LOG_CLOSE_FWD	  16
+#define	LOG_FWD_SLAVE	  32
+#define LOG_ONLINE	  64
+#define LOG_OFFLINE	 128
+#define LOG_INVIS	 256
+#define LOG_LIMIT	 512
+#define	LOG_ALL		1023
+
+#ifndef GR_VERTICAL
+#define GR_VERTICAL	'|'
+#endif
+#ifndef GR_LUCORNER
+#define GR_LUCORNER	'/'
+#endif
+
+#ifndef GR_LBCORNER
+#define GR_LBCORNER	'\\'
+#endif
+
+static int ports[MAX_MASQ_APP_PORTS] = { 4000 }, logmask = LOG_LOGIN;
+static int range[] = { 60200, 61000 };
+static int tcp_timeout = 14400, tcp_fin_timeout = 60, udp_timeout = 600,
+	   icq_port, limit = 512, hide_local_ip = 1, hide_remote_local_ip = 0,
+	   intranet = 1;
+static char *log;
+
+MODULE_DESCRIPTION ("ICQ Masquerading Module");
+MODULE_AUTHOR ("djsf");
+MODULE_PARM (ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+MODULE_PARM (tcp_timeout, "i");
+MODULE_PARM (tcp_fin_timeout, "i");
+MODULE_PARM (udp_timeout, "i");
+MODULE_PARM (range, "2i");
+MODULE_PARM (log, "0-1s");
+MODULE_PARM (limit, "i");
+MODULE_PARM (hide_local_ip, "i");
+MODULE_PARM (hide_remote_local_ip, "i");
+MODULE_PARM (intranet, "i");
+
+static struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+#ifndef __NO_KSPC_CLISTS__
+struct online {
+    struct online *next;
+    __u32	   uin, addr;
+    __u16	   port;
+};
+#endif
+
+static struct client {
+    struct client *next;
+#ifndef __NO_KSPC_CLISTS__
+    struct online *online;
+#endif
+    __u32	   uin, cl_addr;
+    __u16	   cl_port, masq_port;
+#ifndef __NO_KSPC_CLISTS__
+    __u16	   online_n;
+#endif
+} *cl_base = NULL;
+
+struct tcp_direct {
+    __u32 uin, last_seq;
+    __u16 pktlen, pktlen_new;
+    __u8  pktstart, pktstart_new;
+};
+
+/*****************************************************************************/
+
+static struct client *cl_lookup (__u16 port)
+{
+    struct client *p = cl_base;
+    for (; p; p = p->next)
+	if (p->masq_port == port)
+	    return p;
+    return NULL;
+}
+
+#ifdef __LITTLE_ENDIAN
+
+static inline __u16 get_le16 (__u8 *p)
+{
+    return *(__u16 *)p;
+}
+
+static inline __u32 get_le32 (__u8 *p)
+{
+    return *(__u32 *)p;
+}
+
+static inline void put_le16 (__u8 *p, __u16 d)
+{
+    *(__u16 *)p = d;
+}
+
+static inline void put_le32 (__u8 *p, __u32 d)
+{
+    *(__u32 *)p = d;
+}
+
+#else	/* any other bytesex */
+
+static inline __u16 get_le16 (__u8 *p)
+{
+    return (p[1] << 8) + *p;
+}
+
+static inline __u32 get_le32 (__u8 *p)
+{
+    return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | *p;
+}
+
+static inline void put_le16 (__u8 *p, __u16 d)
+{
+    *p++ = d;
+    *p = d >> 8;
+}
+
+static inline void put_le32 (__u8 *p, __u32 d)
+{
+    *p++ = d;
+    *p++ = d >> 8;
+    *p++ = d >> 16;
+    *p = d >> 24;
+}
+#endif
+
+
+static inline int alloc_mport (void)
+{
+    int started_from = icq_port;
+
+    for (; icq_port < icq_end_port; icq_port++)
+	if (!cl_lookup (htons (icq_port)))
+	    return htons (icq_port++);	/* TODO lookup port in masq table too */
+    for (icq_port = icq_start_port; icq_port < started_from; icq_port++)
+	if (!cl_lookup (htons (icq_port)))
+	    return htons (icq_port++);
+
+    return 0;
+}
+
+static inline void v4_put_checkcode (__u8 *data)
+{
+    __u8 *p = data + (*data == 4 ? 16 : 12);
+
+    *p++ = data[6] ^ 0xF5;    /* NUMBER2 == 0x01FF00F5 is valid for all packets of version < 256 :) */
+    *p++ = data[2];
+    *p++ = ~data[4];
+    *p = data[8] ^ 1;
+}
+
+static inline __u32 v5_calc_checkcode (__u8 *data)
+{
+    return ((data[8] ^ 0x1F) << 24) | (0xFF0000&((data[4] ^ ~data[0x1F]) << 16))
+	  | (data[2] << 8) | (data[6] ^ 0xA6);
+}
+
+static inline __u32 v4code2 (__u8 *data, unsigned len)
+{
+    return len*0x66756B65 + get_le32 (data+16);
+}
+
+static inline __u32 v5code2 (unsigned len, __u32 c)
+{
+    return len*0x68656C6C + c;
+}
+
+static __u8 v4table[] = {
+    0x0A, 0x20, 0x20, 0x20, 0x69, 0x74, 0x73, 0x64,
+    0x43, 0x61, 0x2E, 0x73, 0x65, 0x74, 0x6F, 0x73,
+    0x72, 0x74, 0x22, 0x66, 0x6E, 0x2F, 0x63, 0x6E,
+    0x51, 0x20, 0x6D, 0x65, 0x6F, 0x73, 0x6E, 0x65,
+    0x6E, 0x6C, 0x6E, 0x20, 0x64, 0x20, 0x6E, 0x5D,
+    0x6E, 0x72, 0x6D, 0x20, 0x74, 0x73, 0x69, 0x20,
+    0x62, 0x63, 0x6B, 0x20, 0x73, 0x74, 0x65, 0x20,
+    0x6C, 0x6F, 0x6C, 0x65, 0x67, 0x73, 0x20, 0x6F
+};
+
+static __u8 v5table[] = {
+    0x4C, 0x5B, 0x6D, 0x6F, 0x4C, 0x63, 0x5F, 0x43,
+    0x31, 0x4A, 0x67, 0x6C, 0x4D, 0x69, 0x44, 0x48,
+    0x6C, 0x51, 0x6C, 0x55, 0x48, 0x6D, 0x5F, 0x47,
+    0x67, 0x35, 0x6E, 0x6D, 0x5F, 0x4B, 0x62, 0x4D,
+    0x5D, 0x58, 0x58, 0x48, 0x59, 0x31, 0x32, 0x52,
+    0x64, 0x4E, 0x53, 0x30, 0x59, 0x3D, 0x3A, 0x52,
+    0x48, 0x49, 0x46, 0x50, 0x6F, 0x25, 0x54, 0x5D,
+    0x50, 0x3F, 0x69, 0x54, 0x31, 0x00, 0x37, 0x46
+};
+
+static void v4crypt (__u8 *data, unsigned len, __u32 c2)
+{
+    int i;
+    __u8 *p;
+
+    for (p = data, i = 0, len = (len+3)/16; i < len; p += 4, i++)
+	put_le32 (p, get_le32(p) ^ (c2 + v4table[i & 0x3F]));
+    *data++ = 4; *data = 0;
+}
+
+static void v5crypt (__u8 *data, unsigned len, __u32 c2)
+{
+    int i, rest = (len-2)&3;
+    __u8 *p;
+
+    for (p = data+10, i = 0, len = (len-10)/4; i < len; p += 4, i++)
+	put_le32 (p, get_le32(p) ^ (c2 + v5table[i & 0x3F]));
+    if (rest) {
+	c2 += v5table[i & 0x3F];
+	do {
+	    *p++ ^= c2;
+	    c2 >>= 8;
+	} while (--rest);
+    }
+}
+
+static inline __u32 v5_extract_checkcode (__u8 *data)
+{
+    __u32 c = get_le32 (data+20);
+
+    return ((c & 0x0001F000) >> 12) + ((c & 0x07C007C0) >> 1)
+	 + ((c & 0x003E0001) << 10) + ((c & 0xF8000000) >> 16)
+	 + ((c & 0x0000083E) << 15);
+}
+
+static inline void v5_insert_checkcode (__u8 *data, __u32 c)
+{
+    put_le32 (data+20, ((c & 0x0000001F) << 12) + ((c & 0x03E003E0) << 1)
+		     + ((c & 0xF8000400) >> 10) + ((c & 0x0000F800) << 16)
+		     + ((c & 0x041F0000) >> 15));
+}
+
+static void update_tcp_range (void)
+{
+    if (icq_start_port > icq_end_port) {
+	int tmp = icq_start_port;
+	icq_start_port = icq_end_port; icq_end_port = tmp;
+    }
+    printk (KERN_INFO "ip_masq_icq: using TCP port range %u-%u\n", icq_port = icq_start_port, icq_end_port);
+}
+
+/*****************************************************************************/
+
+static struct ip_masq_mod icq_mod;
+static struct ip_masq_timeout_table icq_timeout_table;
+
+static int masq_icq_start (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+    ip_masq_timeout_attach (ms, &icq_timeout_table);
+    if (!(ms->app_data = kmalloc (sizeof (struct client), GFP_ATOMIC)))
+	return -ENOMEM;
+    cl->next = cl_base; cl_base = cl;
+    cl->masq_port = 0;
+#ifndef __NO_KSPC_CLISTS__
+    cl->online = NULL;
+    cl->online_n = 0;
+#endif
+    ip_masq_mod_inc_nent (&icq_mod);
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int masq_icq_fin (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+    ip_masq_mod_dec_nent (&icq_mod);
+    ip_masq_timeout_detach (ms);
+    if (cl) {
+	struct client **p = &cl_base;
+#ifndef __NO_KSPC_CLISTS__
+	struct online *ponl = cl->online, *ponl_next;
+
+	for (; ponl; ponl = ponl_next) {
+	    ponl_next = ponl->next;
+	    kfree (ponl);
+	}
+#endif
+	if (cl->masq_port)
+	    if (logmask & LOG_LOGOUT)
+		printk (KERN_INFO "ip_masq_icq: LOGOUT %u@%u.%u.%u.%u:%u<-%u/%u\n",
+			cl->uin, NIPQUAD (cl->cl_addr), ntohs (cl->cl_port), ntohs (cl->masq_port), ntohs (ms->sport));
+	for (; *p != cl; p = &(*p)->next)
+	    if (!*p) {
+		printk (KERN_ERR "ip_masq_icq: broken client list, prepare for kernel panic\n");
+		return -EFAULT;
+	    }
+	*p = cl->next;
+	kfree (cl); cl = NULL;
+    }
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int masq_icq_pkt_out (struct ip_masq_app *mapp, struct ip_masq *ms,
+			     struct sk_buff **skb_p, __u32 maddr)
+{
+    struct sk_buff *skb = *skb_p;
+    struct iphdr *iph = skb->nh.iph;
+    __u8 *data = (__u8 *)((struct udphdr *)&(((__u8 *)iph)[iph->ihl << 2])+1), *port_ptr;
+    __u16 from_port;
+    __u32 c2;
+    unsigned len = skb->len - (data - skb->h.raw);
+
+    if (data[1] || len < 32 || !cl)
+	return 0;
+    switch (*data) {
+	case 2:	    if (get_le16 (data+2) != 0x3E8) return 0;
+		    port_ptr = data + 10; break;
+	case 3:	    if (get_le16 (data+2) != 0x3E8) return 0;
+		    port_ptr = data + 20; break;
+	case 4:	    c2 = v4code2 (data, len);
+		    if ((get_le16 (data+6) ^ (c2 >> 16)) != 0x3E8) return 0;
+		    port_ptr = data + 24; v4crypt (data, len, c2); break;
+	case 5:	    c2 = v5code2 (len, v5_extract_checkcode (data));
+		    if ((get_le16 (data+14) ^ (0xFFFF&(c2+0x5B))) != 0x3E8) return 0;
+		    port_ptr = data + 28; v5crypt (data, len, c2); break;
+	default:    return 0;
+    }
+    from_port = get_le16 (port_ptr);
+
+    if (cl->masq_port) {
+	if (!from_port) {
+	    cl->masq_port = 0; goto get_out;
+	}
+    } else {
+	if (!from_port)
+	    goto get_out;
+	cl->masq_port = alloc_mport();
+    }
+    cl->cl_addr = ms->saddr; cl->cl_port = htons (from_port);
+
+    port_ptr[0] = ((__u8 *)&cl->masq_port)[1];	/* ICQ order is LE, net order is BE */
+    port_ptr[1] = ((__u8 *)&cl->masq_port)[0];
+
+get_out:
+    if (hide_local_ip && len > get_le16 (data+14)+24)
+	*(__u32 *)(data+get_le16 (data+14)+20) = maddr;
+    switch (*data) {
+	case 2:	 cl->uin = get_le32 (data+6); break;
+	case 3:  cl->uin = get_le32 (data+8);
+		 v4_put_checkcode (data); break;
+	case 4:  cl->uin = get_le32 (data+12);
+		 v4_put_checkcode (data);
+		 v4crypt (data, len, v4code2 (data, len)); break;
+	case 5:	 cl->uin = get_le32 (data+6);
+		 v5crypt (data, len, v5code2 (len, c2 = v5_calc_checkcode (data)));
+		 v5_insert_checkcode (data, c2); break;
+    }
+
+    if (logmask & LOG_LOGIN)
+	printk (KERN_INFO "ip_masq_icq: LOGIN %u@%u.%u.%u.%u:%u<-%u/%u, protocol v%u\n",
+		cl->uin, NIPQUAD (cl->cl_addr), from_port, ntohs (cl->masq_port), ntohs (ms->sport), *data);
+    return 0;
+}
+
+#ifndef __NO_KSPC_CLISTS__
+static void add_online (struct client *pcl, __u8 *param_p, __u32 maddr, int *altered)
+{
+    struct online *ponl;
+    struct client *lcl;
+    char rmt_loc[20];
+    __u32 uin = get_le32 (param_p);
+
+    if (logmask & LOG_ONLINE) {
+	if (*(__u32 *)(param_p+4) == *(__u32 *)(param_p+12))
+	    *rmt_loc = 0;
+	else
+	    sprintf (rmt_loc, " (%u.%u.%u.%u)", NIPQUAD (param_p[12]));
+	printk (KERN_INFO "ip_masq_icq: USER_ONLINE %u@%u.%u.%u.%u%s:%u appears in %u@%u.%u.%u.%u:%u<-%u's contact list\n",
+		uin, NIPQUAD (param_p[4]), rmt_loc, get_le16 (param_p+8), pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (pcl->masq_port));
+    }
+    if (intranet && *(__u32 *)(param_p+4) == maddr && (lcl = cl_lookup (htons (get_le16 (param_p+8))))) {
+	*(__u32 *)(param_p+4) = lcl->cl_addr;
+	put_le16 (param_p+8, ntohs (lcl->cl_port));
+	*altered = 1;
+    }
+    if (hide_remote_local_ip) {
+	*(__u32 *)(param_p+12) = *(__u32 *)(param_p+4);
+	*altered = 1;
+    }
+    for (ponl = pcl->online; ponl; ponl = ponl->next)
+	if (ponl->uin == uin) goto found_online;
+    if (++pcl->online_n > limit) {
+	pcl->online_n = limit;
+	if (logmask & LOG_LIMIT)
+	    printk (KERN_WARNING "ip_masq_icq: online list size limit for %u reached\n", pcl->uin);
+	return;
+    }
+    if (!(ponl = (struct online *)kmalloc (sizeof (struct online), GFP_ATOMIC))) {
+	pcl->online_n--;
+	return;
+    }
+    ponl->next = pcl->online; pcl->online = ponl;
+
+found_online:
+    ponl->uin = uin;
+    ponl->addr = *(__u32 *)(param_p+4);
+    ponl->port = htons (get_le16 (param_p+8));
+}
+
+static void del_online (struct client *pcl, __u8 *param_p)
+{
+    struct online *ponl, **pponl;
+    __u32 uin = get_le32 (param_p);
+
+    for (pponl = &pcl->online; *pponl; pponl = &(*pponl)->next)
+	if ((*pponl)->uin == uin) {
+	    if (logmask & LOG_OFFLINE)
+		printk (KERN_INFO "ip_masq_icq: USER_OFFLINE %u@%u.%u.%u.%u:%u disappears from %u@%u.%u.%u.%u:%u<-%u's contact list\n",
+			uin, NIPQUAD ((*pponl)->addr), ntohs ((*pponl)->port), pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (pcl->masq_port));
+	    *pponl = (ponl = *pponl)->next;
+	    kfree (ponl);
+	    pcl->online_n--;
+	    return;
+	}
+    if (logmask & LOG_INVIS)
+	printk (KERN_INFO "ip_masq_icq: %u is possibly invisible for %u@%u.%u.%u.%u:%u<-%u\n",
+		uin, pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (pcl->masq_port));
+}
+
+static int masq_icq_pkt_in (struct ip_masq_app *mapp, struct ip_masq *ms,
+			    struct sk_buff **skb_p, __u32 maddr)
+{
+    struct sk_buff *skb = *skb_p;
+    struct iphdr *iph = skb->nh.iph;
+    int altered = 0;
+    __u8 *data = (__u8 *)((struct udphdr *)&(((__u8 *)iph)[iph->ihl << 2])+1), *p, *lim;
+    unsigned len = skb->len - (data - skb->h.raw), i, ofs;
+
+    if (data[1] || len < 10 || !cl)
+	return 0;
+    switch (*data) {
+	case 2:	    switch (get_le16 (data+2)) {
+			case 0x6E:  if (len > 30)
+					add_online (cl, data+6, maddr, &altered);
+				    return 0;
+			case 0x78:  del_online (cl, data+6);
+		    }
+	case 3:	    p = data+2; ofs = 4; break;
+	case 5:	    p = data+7; ofs = 9; break;
+	default:    return 0;
+    }
+    switch (get_le16 (p)) {
+	case 0x212: if (len > 28 && (i = (p+=15)[-1]))
+			for (lim = data+len-18;;) {
+			    switch (get_le16 (p+ofs)) {
+				case 0x6E:  if (p+ofs+11 >= lim) goto check_altered;
+					    add_online (cl, p+ofs+14, maddr, &altered);
+					    break;
+				case 0x78:  if (p+ofs >= lim) goto check_altered;
+					    del_online (cl, p+ofs+14);
+			    }
+			    if (!--i || (p += 2 + get_le16 (p)) >= lim) break;
+			}
+		    break;
+	case 0x6E:  if (len > 40)
+			add_online (cl, p+14, maddr, &altered);
+		    break;
+	case 0x78:  if (len > ofs+15)
+    			del_online (cl, p+14);
+    }
+check_altered:
+    if (altered && *data == 3)
+	v4_put_checkcode (data);
+    return 0;
+}
+#else /* __NO_KSPC_CLISTS__ */
+#define masq_icq_pkt_in NULL
+#endif
+
+static struct ip_masq_app ip_masq_icq = {
+    NULL,			/* next		   */
+    "icq",			/* name		   */
+    0,				/* type		   */
+    0,				/* n_attach	   */
+    masq_icq_start,		/* ip_masq_init_1  */
+    masq_icq_fin,		/* ip_masq_done_1  */
+    masq_icq_pkt_out,		/* pkt_out	   */
+    masq_icq_pkt_in		/* pkt_in	   */
+};
+
+/*****************************************************************************/
+
+static struct ip_masq_timeout_table icq_timeout_table = {
+    ATOMIC_INIT(0), /* refcnt */
+    0,		    /* scale  */
+    {
+	    30*60*HZ,	    /*	    IP_MASQ_S_NONE,	    */
+	    15*60*HZ,	    /*	    IP_MASQ_S_ESTABLISHED,  */
+	    2*60*HZ,	    /*	    IP_MASQ_S_SYN_SENT,	    */
+	    1*60*HZ,	    /*	    IP_MASQ_S_SYN_RECV,	    */
+	    2*60*HZ,	    /*	    IP_MASQ_S_FIN_WAIT,	    */
+	    2*60*HZ,	    /*	    IP_MASQ_S_TIME_WAIT,    */
+	    10*HZ,	    /*	    IP_MASQ_S_CLOSE,	    */
+	    60*HZ,	    /*	    IP_MASQ_S_CLOSE_WAIT,   */
+	    30*HZ,	    /*	    IP_MASQ_S_LAST_ACK,	    */
+	    2*60*HZ,	    /*	    IP_MASQ_S_LISTEN,	    */
+	    5*60*HZ,	    /*	    IP_MASQ_S_UDP,	    */
+	    1*60*HZ,	    /*	    IP_MASQ_S_ICMP,	    */
+	    2*HZ,	    /*	    IP_MASQ_S_LAST	    */
+    },      /* timeout */
+};  /* copied from ip_masq.c */
+
+static void update_timeouts (void)
+{
+    icq_timeout_table.timeout[IP_MASQ_S_ESTABLISHED] = HZ*tcp_timeout;
+    icq_timeout_table.timeout[IP_MASQ_S_FIN_WAIT] =
+    icq_timeout_table.timeout[IP_MASQ_S_TIME_WAIT] = HZ*tcp_fin_timeout;
+    icq_timeout_table.timeout[IP_MASQ_S_UDP] = HZ*udp_timeout;
+}
+
+static struct ip_masq_app ip_masq_icq_direct;
+
+static int icq_in_rule (const struct sk_buff *skb, const struct iphdr *iph)
+{
+    if (iph->protocol != IPPROTO_TCP)
+	return IP_MASQ_MOD_NOP;
+    return cl_lookup (((__u16 *)&(((__u8 *)iph)[iph->ihl << 2]))[1]) ? IP_MASQ_MOD_ACCEPT : IP_MASQ_MOD_NOP;
+}
+
+static struct ip_masq *icq_in_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+    __u16 *portp = (__u16 *)&(((__u8 *)iph)[iph->ihl << 2]);
+    struct client *pcl;
+    struct ip_masq *ms;
+    struct tcp_direct *mem;
+
+    if (iph->protocol != IPPROTO_TCP || iph->daddr != maddr || !(pcl = cl_lookup (portp[1])))
+	return NULL;
+
+    if (logmask & LOG_FWD)
+	printk (KERN_INFO "ip_masq_icq: forwarding connection %u.%u.%u.%u:%u -> %u@%u.%u.%u.%u:%u<-%u\n",
+		NIPQUAD (iph->saddr), ntohs (*portp), pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (portp[1]));
+    if (!(mem = kmalloc (sizeof (struct tcp_direct), GFP_ATOMIC)))
+	return NULL;
+    if (!(ms = ip_masq_new (IPPROTO_TCP, maddr, portp[1], pcl->cl_addr, pcl->cl_port, iph->saddr, *portp, 0))) {
+	printk (KERN_WARNING "ip_masq_icq: cannot create masq table entry for incoming connection\n");
+	kfree (mem); return NULL;
+    }
+    ip_masq_timeout_attach (ms, &icq_timeout_table);
+    ms->app = &ip_masq_icq_direct; ms->app_data = mem;
+    ip_masq_icq_direct.n_attach++;
+    mem->uin = pcl->uin;
+    memset (&(mem->last_seq), 0, sizeof (struct tcp_direct) - ((__u8 *)&(mem->last_seq) - (__u8 *)mem));
+    MOD_INC_USE_COUNT;
+    ip_masq_listen (ms);
+    return ms;
+}
+
+#ifndef __NO_KSPC_CLISTS__
+static struct ip_masq *icq_out_create (const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+    __u16 *portp = (__u16 *)&(((__u8 *)iph)[iph->ihl << 2]);
+    struct client *pcl;
+    struct ip_masq *ms;
+    struct online *ponl;
+    struct tcp_direct *mem;
+
+    if (iph->protocol == IPPROTO_TCP)
+	for (pcl = cl_base; pcl; pcl = pcl->next)
+	    if (iph->saddr == pcl->cl_addr)
+		for (ponl = pcl->online; ponl; ponl = ponl->next)
+		    if (iph->daddr == ponl->addr && portp[1] == ponl->port) {
+			if (logmask & LOG_FWD_OUT)
+			    printk (KERN_INFO "ip_masq_icq: forwarding connection %u@%u.%u.%u.%u:%u <- %u@%u.%u.%u.%u:%u\n",
+				    ponl->uin, NIPQUAD (iph->daddr), ntohs (portp[1]), pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (*portp));
+			if (!(mem = kmalloc (sizeof (struct tcp_direct), GFP_ATOMIC)))
+			    return NULL;
+			if (!(ms = ip_masq_new (IPPROTO_TCP, maddr, 0, iph->saddr, *portp, iph->daddr, portp[1], 0))) {
+			    printk (KERN_WARNING "ip_masq_icq: cannot create masq table entry for outgoing connection\n");
+			    kfree (mem); return NULL;
+			}
+			ip_masq_timeout_attach (ms, &icq_timeout_table);
+			ms->app = &ip_masq_icq_direct; ms->app_data = mem;
+			ip_masq_icq_direct.n_attach++;
+			mem->uin = pcl->uin;
+			memset (&(mem->last_seq), 0, sizeof (struct tcp_direct) - ((__u8 *)&(mem->last_seq) - (__u8 *)mem));
+			MOD_INC_USE_COUNT;
+			return ms;
+		    }
+    return NULL;
+}
+#else /* __NO_KSPC_CLISTS__ */
+#define icq_out_create NULL
+#endif
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry icq_proc_root;
+#define icq_proc_root_p &icq_proc_root
+#else /* ! CONFIG_PROC_FS */
+#define icq_proc_root_p NULL
+#endif
+
+static struct ip_masq_mod icq_mod = {
+    NULL,			/* next		   */
+    NULL,			/* next_reg	   */
+    "icq",			/* mmod_name	   */
+    ATOMIC_INIT(0),		/* refcnt	   */
+    ATOMIC_INIT(0),		/* mmod_nent	   */
+    icq_proc_root_p,		/* mmod_proc_ent   */
+    NULL,			/* mmod_ctl	   */
+    NULL,			/* mmod_init	   */
+    NULL,			/* mmod_done	   */
+    icq_in_rule,		/* mmod_in_rule	   */
+    NULL,			/* mmod_in_update  */
+    icq_in_create,		/* mmod_in_create  */
+    NULL,			/* mmod_out_rule   */
+    NULL,			/* mmod_out_update */
+    icq_out_create		/* mmod_out_create */
+};
+
+/*****************************************************************************/
+
+static int icq_direct_fin (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+    if (logmask & LOG_CLOSE_FWD) {
+	int mp = ntohs (ms->mport);
+	printk (KERN_INFO "ip_masq_icq: closing connection %u.%u.%u.%u:%u %s %u@%u.%u.%u.%u:%u<-%u\n",
+    		NIPQUAD (ms->daddr), ntohs (ms->dport), PORT_MASQ_BEGIN <= mp && mp <= PORT_MASQ_END ? "<-" : "->",
+		tcpdir->uin, NIPQUAD (ms->saddr), ntohs (ms->sport), mp);
+    }
+    if (ms->app_data) {
+	kfree (ms->app_data); ms->app_data = NULL;
+    }
+    ip_masq_timeout_detach (ms);
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int icq_direct_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+    struct ip_masq *fcms;
+    struct sk_buff *skb = *skb_p;
+    struct iphdr *iph = skb->nh.iph;
+    struct tcphdr *th = (struct tcphdr *)&(((__u8 *)iph)[iph->ihl << 2]);
+    __u8 *data = (__u8 *)th + (th->doff << 2), *p;
+    __u16 fcport, pktlen;
+    unsigned len = skb->len - (data - skb->h.raw), st;
+
+    if (th->syn || th->fin || th->rst)
+	return 0;
+
+    if (tcpdir->last_seq != th->seq) {
+	tcpdir->last_seq = th->seq;
+	pktlen = tcpdir->pktlen = tcpdir->pktlen_new;
+	tcpdir->pktstart = tcpdir->pktstart_new;
+    } else
+	pktlen = tcpdir->pktlen;
+
+    if (tcpdir->pktstart) {
+	tcpdir->pktstart_new = 0; goto pktstart;
+    }
+
+    while (pktlen < len) {
+	len -= pktlen;
+	if (len < 2) {				/* TODO */
+	    printk (KERN_WARNING "ip_masq_icq: %u@%u.%u.%u.%u:%u's client is bad and mad and not supported yet\n", tcpdir->uin, NIPQUAD (ms->saddr), ntohs (ms->sport));
+	    pktlen = 65535; goto get_out;
+	}
+	pktlen = get_le16 (data += pktlen);
+	if (!(len -= 2)) {
+	    tcpdir->pktstart_new = 1; goto get_out;
+	}
+	data += 2;
+pktstart:
+	if (len > 41) {
+	    if (get_le16 (data+6) == 0x7DA)			/* v3 */
+		p = data+14;
+	    else if (((__u16 *)data)[1] == ((__u16 *)data)[8])	/* ICQ98 */
+		p = data+18;
+	    else if ((st = get_le16 (data+22)) == 2		/* ICQ99 chat */
+		      && *(__u16 *)(data+34)
+		      && ntohs (*(__u16 *)(data+34)) == get_le16 (data+38)) {
+		p = data+9; fcport = *(__u16 *)(data+34);
+		if (logmask & LOG_FWD_SLAVE)
+		    printk (KERN_INFO "ip_masq_icq: chat request from %u.%u.%u.%u:%u accepted by %u@%u.%u.%u.%u:%u using ICQ99 protocol\n",
+			    NIPQUAD (ms->daddr), ntohs (ms->dport), tcpdir->uin, NIPQUAD (ms->saddr), ntohs (fcport));
+		goto create_secondary;
+	    } else if (st == 3					/* ICQ99 file xfer */
+		       && *(__u16 *)(data+31) && get_le16 (data+35) + 42 < len
+		       && ntohs (*(__u16 *)(data+31)) == get_le16 (data+get_le16 (data+35) + 41)) {
+		p = data+9; fcport = *(__u16 *)(data+31);
+		if (logmask & LOG_FWD_SLAVE)
+		    printk (KERN_INFO "ip_masq_icq: file xfer request from %u.%u.%u.%u:%u accepted by %u@%u.%u.%u.%u:%u using ICQ99 protocol\n",
+			    NIPQUAD (ms->daddr), ntohs (ms->dport), tcpdir->uin, NIPQUAD (ms->saddr), ntohs (fcport));
+		goto create_secondary;
+	    } else
+		continue;
+	    if (get_le16 (p+2) != 1 || len < 49) /* TODO support msglengths other than 1 */
+		continue;
+	    switch (st = get_le16 (p)) {
+		case 2:	    if (!(fcport = *(__u16 *)(p+25)))
+				continue;
+			    if (logmask & LOG_FWD_SLAVE)
+				printk (KERN_INFO "ip_masq_icq: chat request from %u.%u.%u.%u:%u accepted by %u@%u.%u.%u.%u:%u using protocol v%u\n",
+					NIPQUAD (ms->daddr), ntohs (ms->dport), tcpdir->uin, NIPQUAD (ms->saddr), ntohs (fcport), get_le16 (data+4));
+			    break;
+		case 3:	    if (!(fcport = *(__u16 *)(p+22)))
+				continue;
+			    if (logmask & LOG_FWD_SLAVE)
+				printk (KERN_INFO "ip_masq_icq: file xfer request from %u.%u.%u.%u:%u accepted by %u@%u.%u.%u.%u:%u using protocol v%u\n",
+					NIPQUAD (ms->daddr), ntohs (ms->dport), tcpdir->uin, NIPQUAD (ms->saddr), ntohs (fcport), get_le16 (data+4));
+			    break;
+		default:    continue;
+	    }
+create_secondary:
+	    if (!(fcms = ip_masq_new (IPPROTO_TCP, maddr, 0, ms->saddr, fcport, ms->daddr, 0, IP_MASQ_F_NO_DPORT))) {
+		printk (KERN_WARNING "ip_masq_icq: cannot create masq table entry for slave connection\n");
+		continue;
+	    }
+	    if (st == 2) {				/* chat	 */
+		*(__u16 *)(p+25) = fcms->mport;
+		put_le16 (p+29, ntohs (fcms->mport));
+		ip_masq_timeout_attach (fcms, &icq_timeout_table); /* TODO detach it somewhere */
+	    } else {					/* file xfer */
+		*(__u16 *)(p+22) = fcms->mport;
+		if ((st = get_le16 (p+26) + 32) < len-1) {
+		    put_le16 (p + st, ntohs (fcms->mport));
+		}
+	    }
+	    ip_masq_control_add (fcms, ms);
+	    ip_masq_listen (fcms); ip_masq_put (fcms);
+	}
+    }
+    pktlen -= len;
+get_out:
+    tcpdir->pktlen_new = pktlen;
+    return 0;
+}
+
+static struct ip_masq_app ip_masq_icq_direct = {
+    NULL,			/* next		   */
+    "icq_direct",		/* name		   */
+    0,				/* type		   */
+    0,				/* n_attach	   */
+    NULL,			/* ip_masq_init_1  */
+    icq_direct_fin,		/* ip_masq_done_1  */
+    icq_direct_out,		/* pkt_out	   */
+    NULL			/* pkt_in	   */
+};
+
+/*****************************************************************************/
+
+#ifdef CONFIG_PROC_FS
+static int read_onoff (char *buffer, char **start, off_t offset,
+			int count, int *eof, void *data)
+{
+    *buffer = 'o';
+    if (*(int *)data) {
+	buffer[1] = 'n'; buffer[2] = '\n'; return 3;
+    }
+    buffer[1] = buffer[2] = 'f'; buffer[3] = '\n'; return 4;
+}
+
+static int write_onoff (struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+    if (count == 1 || (count == 2 && buffer[1] == '\n'))
+	switch (*buffer) {
+clr_data:   case '0':   *(int *)data = 0; return count;
+set_data:   case '1':   *(int *)data = 1; return count;
+	    default:    return -EINVAL;
+	}
+    if (*buffer == 'o') {
+	if (buffer[1] == 'n' && (count == 2 || (count == 3 && buffer[2] == '\n')))
+	    goto set_data;
+	if (buffer[1] == 'f' && buffer[2] == 'f' && (count == 3 || (count == 4 && buffer[3] == '\n')))
+	    goto clr_data;
+    }
+    return -EINVAL;
+}
+
+static int read_int (char *buffer, char **start, off_t offset,
+			int count, int *eof, void *data)
+{
+    return sprintf (buffer, "%u\n", *(int *)data);
+}
+
+static int write_int (struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+    char *p, buff[16];
+    unsigned long l;
+
+    if (!isdigit (*buffer))
+	return -EINVAL;
+    l = simple_strtoul (strncpy (buff, buffer, 16), &p, 0);
+    if (*p && *p != '\t' && *p != '\n' && *p != ' ')
+	return -EINVAL;
+    *(int *)data = l;
+    update_timeouts();
+    return count;
+}
+
+static int read_range (char *buffer, char **start, off_t offset,
+			int count, int *eof, void *data)
+{
+    return sprintf (buffer, "%u-%u\n", *(int *)data, ((int *)data)[1]);
+}
+
+static int write_range (struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+    char *p, buff[16];
+    unsigned long first, second;
+
+    if (!isdigit (*buffer))
+	return -EINVAL;
+    first = simple_strtoul (strncpy (buff, buffer, 16), &p, 0);
+    while (*p == '\t' || *p == ' ' || *p == ',' || *p == '-')
+	p++;
+    if (!isdigit (*p))
+	return -EINVAL;
+    second = simple_strtoul (p, &p, 0);
+    if (*p && *p != '\t' && *p != '\n' && *p != ' ')
+	return -EINVAL;
+    *(int *)data = first;
+    ((int *)data)[1] = second;
+    update_tcp_range();
+    return count;
+}
+
+static inline char check_log_opt (int mask)
+{
+    return logmask & mask ? '+' : '-';
+}
+
+static int read_log (char *buffer, char **start, off_t offset,
+			int count, int *eof, void *data)
+{
+    return sprintf (buffer, "%co Client Logout\n"
+			    "%ci Client Login\n"
+			    "%cf Forwarding incoming direct connection\n"
+			    "%cd Forwarding outgoing direct connection\n"
+			    "%cc Closing the forwarded connection\n"
+			    "%cs Forwarding secondary connection\n"
+			    "%cl User on client's contact list is online\n"
+			    "%cn User on client's contact list is offline\n"
+			    "%cx User is _possibly_ invisible for client\n"
+			    "%cm Online users list for specified client is overflowed\n%s",
+			    check_log_opt (LOG_LOGOUT), check_log_opt (LOG_LOGIN),
+			    check_log_opt (LOG_FWD), check_log_opt (LOG_FWD_OUT),
+			    check_log_opt (LOG_CLOSE_FWD), check_log_opt (LOG_FWD_SLAVE),
+			    check_log_opt (LOG_ONLINE), check_log_opt (LOG_OFFLINE),
+			    check_log_opt (LOG_INVIS), check_log_opt (LOG_LIMIT),
+			    logmask == LOG_ALL ? "+a All logging options are turned on\n" : "");
+}
+#endif	/* CONFIG_PROC_FS */
+
+static int write_log (struct file *file, const char *optstr,
+			unsigned long count, void *data)
+{
+    int lm = logmask, opmask=0, len;
+    enum {
+	PUT=0, CLR, SET
+    } op = PUT;
+
+    void apply_opt (void)
+    {
+	switch (op) {
+	    case PUT:	if (opmask || (len & (1 << (8*sizeof (len) - 1)) && !*optstr))
+			    lm = opmask; break;
+	    case CLR:	lm &= ~opmask; break;
+	    case SET:	lm |= opmask; break;
+	}
+	opmask = 0;
+    }
+
+    for (len = count; len && *optstr; len--, optstr++)
+	switch (*optstr) {
+	    case 'o':   opmask |= LOG_LOGOUT;	 break;
+	    case 'i':   opmask |= LOG_LOGIN;	 break;
+	    case 'f':   opmask |= LOG_FWD;	 break;
+	    case 'd':   opmask |= LOG_FWD_OUT;	 break;
+	    case 'c':   opmask |= LOG_CLOSE_FWD; break;
+	    case 's':   opmask |= LOG_FWD_SLAVE; break;
+	    case 'l':   opmask |= LOG_ONLINE;	 break;
+	    case 'n':   opmask |= LOG_OFFLINE;	 break;
+	    case 'x':   opmask |= LOG_INVIS;	 break;
+	    case 'm':   opmask |= LOG_LIMIT;	 break;
+	    case 'a':   opmask |= LOG_ALL;
+	    case '\t':
+	    case '\n':
+	    case ' ':	break;
+	    case '+':	apply_opt(); op = SET; break;
+	    case '-':	apply_opt(); op = CLR; break;
+	    default:    printk (KERN_WARNING "ip_masq_icq: unknown log option `%c'\n", *optstr);
+			return -EINVAL;
+	}
+    apply_opt();
+    logmask = lm;
+    return count;
+}
+
+#ifdef CONFIG_PROC_FS
+static int read_users (char *buffer, char **start, off_t offset,
+			int count, int *eof, void *data)
+{
+    off_t pos=0;
+    struct client *pcl;
+#ifndef __NO_KSPC_CLISTS__
+    struct online *ponl;
+#endif
+
+    for (pcl = cl_base; pcl; pcl = pcl->next) {
+	if (!pcl->masq_port)
+	    continue;
+	if ((pos += 41) > offset) {
+#ifdef __NO_KSPC_CLISTS__
+	    buffer += sprintf (buffer, " %10u@%3u.%3u.%3u.%3u:%5u<-%5u\n", pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (pcl->masq_port));
+#else
+	    buffer += sprintf (buffer, "%c%10u@%3u.%3u.%3u.%3u:%5u<-%5u\n", pcl->online ? GR_LUCORNER : ' ', pcl->uin, NIPQUAD (pcl->cl_addr), ntohs (pcl->cl_port), ntohs (pcl->masq_port));
+#endif
+	    if (pos >= offset+count)
+		goto get_out;
+	}
+#ifndef __NO_KSPC_CLISTS__
+	for (ponl = pcl->online; ponl; ponl = ponl->next)
+	    if ((pos += 73) > offset) {
+		buffer += sprintf (buffer, "%c%49u@%3u.%3u.%3u.%3u:%5u\n", ponl->next ? GR_VERTICAL : GR_LBCORNER, ponl->uin, NIPQUAD (ponl->addr), ntohs (ponl->port));
+		if (pos >= offset+count)
+		    goto get_out;
+	    }
+#endif
+    }
+    *eof = 1;
+get_out:
+    *start = buffer + offset - pos;
+    return (pos -= offset) > count ? count : pos;
+}
+
+static int read_version (char *buffer, char **start, off_t offset,
+			 int count, int *eof, void *data)
+{
+    return sprintf (buffer, "ip_masq_icq-0.56\n");
+}
+
+#ifndef __DONT_LOCK__
+static int icq_proc_open (struct inode *inode, struct file *file)
+{
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int icq_proc_close (struct inode *inode, struct file *file)
+{
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static struct file_operations icq_proc_file_operations = {
+    NULL,		/* lseek -- copy from proc_file_operations */
+    NULL,		/* read  -- copy from proc_file_operations */
+    NULL,		/* write -- copy from proc_file_operations */
+    NULL,		/* readdir */
+    NULL,		/* poll    */
+    NULL,		/* ioctl   */
+    NULL,		/* mmap    */
+    icq_proc_open,
+    NULL,		/* flush   */
+    icq_proc_close
+};
+
+static struct inode_operations icq_proc_inode_operations = {
+    &icq_proc_file_operations
+};
+#endif /* !__DONT_LOCK__ */
+
+#define ino_ops NULL
+#define declare_proc_entry(opt,type)	proc_##opt = {		\
+    0, sizeof (#opt) - 1, #opt, S_IFREG | S_IRUGO | S_IWUSR,	\
+    1, 0, 0, 0,		/* nlink, uid, gid, size	 */	\
+    ino_ops, NULL, NULL,/* ino_ops, get_info, fill_inode */	\
+    NULL, NULL, NULL,	/* next, parent, subdir		 */	\
+    &opt, read_##type, write_##type				\
+}
+
+static struct proc_dir_entry declare_proc_entry (tcp_timeout, int),
+#ifndef __DONT_LOCK__
+#undef ino_ops
+#define ino_ops &icq_proc_inode_operations
+#endif
+			     declare_proc_entry (tcp_fin_timeout, int),
+			     declare_proc_entry (udp_timeout, int),
+			     declare_proc_entry (range, range),
+			     declare_proc_entry (log, log),
+			     declare_proc_entry (limit, int),
+			     declare_proc_entry (hide_local_ip, onoff),
+			     declare_proc_entry (hide_remote_local_ip, onoff),
+			     declare_proc_entry (intranet, onoff),
+			     icq_proc_users = {
+				0, 5, "users", S_IFREG | S_IRUSR, 1, 0, 0, 0,
+				ino_ops, NULL, NULL, NULL, NULL, NULL, NULL, read_users
+			     },
+			     icq_proc_version = {
+				0, 7, "version", S_IFREG | S_IRUSR, 1, 0, 0, 0,
+				ino_ops, NULL, NULL, NULL, NULL, NULL, NULL, read_version
+			     },
+			     icq_proc_root = {
+				0, 3, "icq", S_IFDIR | S_IRUGO | S_IXUGO, 2
+			     };
+
+static inline void icq_proc_register (void)
+{
+    proc_register (&icq_proc_root, &proc_tcp_timeout);	/* empty (NULL) ops field is filled with &proc_file_operations */
+#ifndef __DONT_LOCK__
+    memcpy (&icq_proc_file_operations, proc_tcp_timeout.ops->default_file_ops, 3*sizeof (void *));
+    proc_tcp_timeout.ops = &icq_proc_inode_operations;
+#endif
+    proc_register (&icq_proc_root, &proc_tcp_fin_timeout);
+    proc_register (&icq_proc_root, &proc_udp_timeout);
+    proc_register (&icq_proc_root, &proc_range);
+    proc_register (&icq_proc_root, &proc_log);
+    proc_register (&icq_proc_root, &proc_limit);
+    proc_register (&icq_proc_root, &proc_hide_local_ip);
+    proc_register (&icq_proc_root, &proc_hide_remote_local_ip);
+    proc_register (&icq_proc_root, &proc_intranet);
+    proc_register (&icq_proc_root, &icq_proc_users);
+    proc_register (&icq_proc_root, &icq_proc_version);
+}
+
+static inline void icq_proc_unregister (void)
+{
+    proc_unregister (&icq_proc_root, proc_tcp_timeout.low_ino);
+    proc_unregister (&icq_proc_root, proc_tcp_fin_timeout.low_ino);
+    proc_unregister (&icq_proc_root, proc_udp_timeout.low_ino);
+    proc_unregister (&icq_proc_root, proc_range.low_ino);
+    proc_unregister (&icq_proc_root, proc_log.low_ino);
+    proc_unregister (&icq_proc_root, proc_limit.low_ino);
+    proc_unregister (&icq_proc_root, proc_hide_local_ip.low_ino);
+    proc_unregister (&icq_proc_root, proc_hide_remote_local_ip.low_ino);
+    proc_unregister (&icq_proc_root, proc_intranet.low_ino);
+    proc_unregister (&icq_proc_root, icq_proc_users.low_ino);
+    proc_unregister (&icq_proc_root, icq_proc_users.low_ino);
+}
+#endif /* CONFIG_PROC_FS */
+
+/*****************************************************************************/
+
+int init_module (void)
+{
+    int i, ret;
+
+    if (log && write_log (NULL, log, ~0, NULL) == -EINVAL)
+	return -EINVAL;
+
+    update_tcp_range();
+    update_timeouts();
+
+    if ((ret = register_ip_masq_mod (&icq_mod))) {
+	printk (KERN_WARNING "ip_masq_icq: cannot register masq mod\n");
+	return ret;
+    }
+    for (i=0; i < MAX_MASQ_APP_PORTS; i++) {
+	if (ports[i]) {
+	    if (!(masq_incarnations[i] = kmalloc (sizeof (struct ip_masq_app), GFP_KERNEL))) {
+		while (i)
+		    kfree (masq_incarnations[--i]);
+		unregister_ip_masq_mod (&icq_mod);
+		return -ENOMEM;
+	    }
+	    memcpy (masq_incarnations[i], &ip_masq_icq, sizeof (struct ip_masq_app));
+	    if ((ret = register_ip_masq_app (masq_incarnations[i], IPPROTO_UDP, ports[i]))) {
+		printk (KERN_WARNING "ip_masq_icq: cannot register support on port %u/UDP\n", ports[i]);
+		unregister_ip_masq_mod (&icq_mod);
+		return ret;
+	    }
+	    printk (KERN_INFO "ip_masq_icq: loaded support on port %u/UDP\n", ports[i]);
+	} else
+	    masq_incarnations[i] = NULL;
+    }
+#ifdef CONFIG_PROC_FS
+    icq_proc_register();
+#endif
+    return 0;
+}
+
+void cleanup_module (void)
+{
+    int i;
+
+#ifdef CONFIG_PROC_FS
+    icq_proc_unregister();
+#endif
+
+    for (i=0; i < MAX_MASQ_APP_PORTS; i++) {
+	if (masq_incarnations[i]) {
+	    if (unregister_ip_masq_app(masq_incarnations[i]))
+		printk (KERN_WARNING "ip_masq_icq: cannot unregister support on port %u/UDP\n", ports[i]);
+	    else {
+		kfree (masq_incarnations[i]);
+		masq_incarnations[i] = NULL;
+		printk (KERN_INFO "ip_masq_icq: unloaded support on port %u/UDP\n", ports[i]);
+	    }
+	}
+    }
+    if (unregister_ip_masq_mod (&icq_mod))
+	printk (KERN_WARNING "ip_masq_icq: cannot unregister masq mod\n");
+}
+
+/* vim: set ts=8 sw=4: */
diff -uPr linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_mms.c linux/net/ipv4/ip_masq_mms.c
--- linux-2.2.19-LEAF-orig/net/ipv4/ip_masq_mms.c	Wed Dec 31 18:00:00 1969
+++ linux/net/ipv4/ip_masq_mms.c	Tue Jul 10 13:54:09 2001
@@ -0,0 +1,413 @@
+/*
+ * IP_MASQ_MMS - Microsoft Messenger Service masquerading module for Linux
+ * Author:  Tom Marshall <tommy@tig-grr.com>
+ * Version: 0.91
+ *
+ *	 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.
+ *
+ * Revision History
+ * ================
+ * v0.91: Fixed bug in skbuff replacement (thanks for the repro David).
+ *        Removed harmless printk warning.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+
+#include <net/ip_masq.h>
+
+/* The default port list */
+static int ports[MAX_MASQ_APP_PORTS] =
+{
+    1863,
+    0
+}; 
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+#define IS_DIGIT(c)    (c >= '0' && c <= '9')
+#define IS_DIGITDOT(c) ((c >= '0' && c <= '9') || c == '.')
+#define IS_ALPHA(c)    ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+#define IS_WSPACE(c)   (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+#define SKIP_WSPACE(p,n) while( n > 0 && IS_WSPACE(*p) ) { n--; p++; }
+
+static struct ip_masq_app* g_masq_objs[MAX_MASQ_APP_PORTS];
+
+/*** helpers ***/
+
+static int
+str_to_u16( char* pbuf, __u16* pval )
+{
+    int n = 0;
+
+    *pval = 0;
+    while( pbuf[n] >= '0' && pbuf[n] <= '9' )
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+
+static int
+str_to_u32( char* pbuf, __u32* pval )
+{
+    int n = 0;
+
+    *pval = 0;
+    while( pbuf[n] >= '0' && pbuf[n] <= '9' )
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+
+static int
+next_line( char** ppbuf, __u32* pbuflen, char** ppline, __u32* plinelen )
+{
+    char* pbuf = *ppbuf;
+    __u32 buflen = *pbuflen;
+    __u32 linelen = 0;
+
+    do
+    {
+        while( *pbuf != '\r' && *pbuf != '\n' )
+        {
+            if( buflen <= 1 )
+            {
+                return 0;
+            }
+
+            pbuf++;
+            linelen++;
+            buflen--;
+        }
+
+        if( buflen > 1 && *pbuf == '\r' && *(pbuf+1) == '\n' )
+        {
+            pbuf++;
+            buflen--;
+        }
+
+        pbuf++;
+        buflen--;
+    }
+    while( buflen > 0 && (*pbuf == ' ' || *pbuf == '\t') );
+
+    *ppline = *ppbuf;
+    *plinelen = linelen;
+    *ppbuf = pbuf;
+    *pbuflen = buflen;
+
+    return linelen;
+}
+
+static void
+get_skb_data( struct sk_buff** ppskb, char** ppdata, __u32* plen )
+{
+    struct sk_buff* skb;
+    struct iphdr*   iph;
+    struct tcphdr*  th;
+    char* pbuf;
+    char* pend;
+
+    skb = *ppskb;
+    iph = skb->nh.iph;
+    th = (struct tcphdr*)&(((char*)iph)[iph->ihl*4]);
+
+    pbuf = (char*)th + (th->doff * 4);
+    pend = skb->h.raw + skb->len;
+    *ppdata = pbuf;
+    *plen = pend - pbuf;
+}
+
+/**************************************
+ * masq funcs
+ **************************************/
+
+static int
+masq_mms_init_1( struct ip_masq_app* pmapp, struct ip_masq* pms )
+{
+    MOD_INC_USE_COUNT;
+
+    pms->app_data = NULL; /* Not using app_data */
+
+    return 0;
+}
+
+static int
+masq_mms_done_1( struct ip_masq_app* pmapp, struct ip_masq* pms )
+{
+    MOD_DEC_USE_COUNT;
+
+    /* If we were using app_data, we would free it here */
+
+    return 0;
+}
+
+/*
+ * Handle incoming (S->C) packet
+ */
+static int
+masq_mms_in( struct ip_masq_app* papp, struct ip_masq* pms, struct sk_buff** ppskb, __u32 maddr )
+{
+    /* Nothing to do here */
+    return 0;
+}
+
+/*
+ * Handle outgoing (C->S) packet
+ */
+static int
+masq_mms_out( struct ip_masq_app* papp, struct ip_masq* pms, struct sk_buff** ppskb, __u32 maddr )
+{
+    int   diff     = 0;
+    char* skbdata  = NULL;
+    __u32 skblen   = 0;
+
+    char* pdata    = NULL;
+    int   datalen  = 0;
+
+    char* pline;
+    __u32 linelen;
+    char  buf[32];
+    int   buflen;
+
+    __u32 entitylen = 0;
+    int isinvite = 0;
+    int accepted = 0;
+    int addrpos = 0;
+    int addrlen = 0;
+    int portpos = 0;
+    int portlen = 0;
+    struct ip_masq* pmsnew;
+    __u16 s_port;
+
+    get_skb_data( ppskb, &skbdata, &skblen );
+    pdata = skbdata;
+    datalen = skblen;
+
+    if( !next_line( &pdata, &datalen, &pline, &linelen ) )
+    {
+        return 0;
+    }
+
+    /* Look for "MSG <seqno> <v> <mimelen>" */
+    if( strncmp( pline, "MSG ", 4 ) != 0 ) return 0;
+    pline += 4;
+    if( ! IS_DIGIT(*pline) ) return 0;
+    while( IS_DIGIT(*pline) ) pline++;
+    if( *pline != ' ' ) return 0;
+    pline++;
+    if( ! IS_ALPHA(*pline) ) return 0;
+    pline++;
+    if( *pline != ' ' ) return 0;
+    pline++;
+    if( ! IS_DIGIT(*pline) ) return 0;
+    pline += str_to_u32( pline, &entitylen );
+
+    /* Parse mime headers */
+    while( next_line( &pdata, &datalen, &pline, &linelen ) )
+    {
+        if( linelen == 0 )
+        {
+            break;
+        }
+        if( memcmp( pline, "Content-Type:", 13 ) == 0 )
+        {
+            pline += 13;
+            while( *pline == ' ' ) pline++;
+            if( memcmp( pline, "text/x-msmsgsinvite;", 20 ) == 0 )
+                isinvite = 1;
+        }
+    }
+    if( !isinvite ) return 0;
+
+    /* Parse entity for accept, ipaddr, port */
+    while( next_line( &pdata, &datalen, &pline, &linelen ) )
+    {
+        if( memcmp( pline, "Invitation-Command:", 19 ) == 0 )
+        {
+            pline += 19;
+            while( *pline == ' ' ) pline++;
+            if( memcmp( pline, "ACCEPT", 6 ) == 0 )
+                accepted = 1;
+        }
+        if( memcmp( pline, "IP-Address:", 11 ) == 0 )
+        {
+            pline += 11;
+            while( *pline == ' ' ) pline++;
+            addrpos = pline - skbdata;
+            addrlen = 0;
+            while( IS_DIGITDOT(pline[addrlen]) ) addrlen++;
+        }
+        if( memcmp( pline, "Port:", 5 ) == 0 )
+        {
+            pline += 5;
+            while( *pline == ' ' ) pline++;
+            portpos = pline - skbdata;
+            portlen = 0;
+            while( IS_DIGIT(pline[portlen]) ) portlen++;
+        }
+    }
+
+    if( !accepted || !addrpos || !portpos ) return 0;
+
+    str_to_u16( skbdata+portpos, &s_port );
+
+    /* Grab a port */
+    pmsnew = ip_masq_new( IPPROTO_TCP,
+                          maddr, 0,
+                          pms->saddr, htons(s_port),
+                          0, 0,
+                          IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR );
+    if( !pmsnew ) return 0;
+
+    /* Replace the data */
+    buflen = sprintf( buf, "%d.%d.%d.%d", NIPQUAD(pmsnew->maddr) );
+    if( buflen == addrlen )
+    {
+        memcpy( skbdata+addrpos, buf, buflen );
+    }
+    else
+    {
+        *ppskb = ip_masq_skb_replace( *ppskb, GFP_ATOMIC,
+                                      skbdata+addrpos, addrlen,
+                                      buf, buflen );
+        get_skb_data( ppskb, &skbdata, &skblen );
+        diff += buflen - addrlen;
+        if( portpos > addrpos )
+        {
+            portpos += buflen - addrlen;
+        }
+    }
+    buflen = sprintf( buf, "%hu", ntohs(pmsnew->mport) );
+    if( buflen == portlen )
+    {
+        memcpy( skbdata+portpos, buf, buflen );
+    }
+    else
+    {
+        *ppskb = ip_masq_skb_replace( *ppskb, GFP_ATOMIC,
+                                      skbdata+portpos, portlen,
+                                      buf, buflen );
+        /* no need to call get_skb_data, we are done */
+        diff += buflen - portlen;
+    }
+
+    /* The operation is a success! */
+    ip_masq_listen( pmsnew );
+    ip_masq_put( pmsnew );
+
+    return diff;
+}
+
+/*** Module load/unload support ***/
+
+static struct ip_masq_app g_masq_functbl =
+{
+    NULL,               /* next */
+    "masq_mms_name",    /* name */
+    0,                  /* type */
+    0,                  /* n_attach */
+    masq_mms_init_1,    /* ip_masq_init_1 */
+    masq_mms_done_1,    /* ip_masq_done_1 */
+    masq_mms_out,       /* pkt_out */
+    masq_mms_in         /* pkt_in */
+};
+
+/*
+ * Initialization (load) function
+ */
+static int __init
+masq_mms_init( void )
+{
+    int i;
+
+    memset( g_masq_objs, 0, sizeof(g_masq_objs) );
+    for( i = 0; i < MAX_MASQ_APP_PORTS && ports[i]; i++ )
+    {
+        int s;
+        g_masq_objs[i] = kmalloc( sizeof(struct ip_masq_app), GFP_KERNEL );
+        if( ! g_masq_objs[i] )
+        {
+            return -ENOMEM;
+        }
+        memcpy( g_masq_objs[i], &g_masq_functbl, sizeof(struct ip_masq_app) );
+        s = register_ip_masq_app( g_masq_objs[i], IPPROTO_TCP, ports[i] );
+        if( s )
+        {
+            return s;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Destruction (unload) function
+ */
+static int
+masq_mms_done( void )
+{
+    int i;
+    int rv;
+
+    rv = 0;
+    for( i = 0; i < MAX_MASQ_APP_PORTS; i++ )
+    {
+        if( g_masq_objs[i] )
+        {
+            int s = unregister_ip_masq_app( g_masq_objs[i] );
+            if( s )
+            {
+                rv = s;
+            }
+            else
+            {
+                kfree( g_masq_objs[i] );
+                g_masq_objs[i] = NULL;
+            }
+        }
+    }
+
+    return rv;
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+
+int init_module( void )
+{
+    if( masq_mms_init() != 0 )
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+void cleanup_module( void )
+{
+    if( masq_mms_done() != 0 )
+    {
+        printk( KERN_INFO "ip_masq_mms: can't remove module" );
+    }
+}
+
+#endif /* MODULE */
