? .hg
? .hgignore
? a
? aclocal.m4
? autom4te.cache
? b
? backtrace.txt
? bug-3919-comment.2010.07.16.txt
? cache
? configure.lineno
? ircd.log
? ircd.pid
? ircd.tune
? motd.txt
? patches
? robots.txt
? server.cert.pem
? server.key.pem
? server.req.pem
? smotd.txt
? tmp
? todo
? tre-0.8.0.tar.gz
? unreal-2321-ipv6-clones.patch
? unrealircd.conf
? doc/AntiRandom
? extras/c-ares
? extras/c-ares-1.6.0.tar
? extras/c-ares-1.6.0.tar.gz
? extras/c-ares.tar
? extras/regexp
? extras/tre-0.7.5
? extras/tre-0.8.0.tar
? extras/tre-0.8.0.tar.gz
? extras/tre.tar
? src/modules/AntiRandom-1.1
? src/modules/AntiRandom.tar.gz
? src/modules/antirandom.c
? src/modules/m_dnsbl.c
? src/modules/m_soper.c
Index: doc/unreal32docs.html
===================================================================
RCS file: /cvs/unreal/doc/Attic/unreal32docs.html,v
retrieving revision 1.1.2.144.2.141
diff -u -B -r1.1.2.144.2.141 unreal32docs.html
--- doc/unreal32docs.html	22 Jul 2010 09:41:56 -0000	1.1.2.144.2.141
+++ doc/unreal32docs.html	13 Aug 2010 04:48:19 -0000
@@ -824,6 +824,7 @@
 	class &lt;connection-class&gt;;
 	password &lt;connection-password&gt; { &lt;auth-type&gt;; };
 	maxperip &lt;max-connections-per-ip&gt;;
+	ipv6-clone-mask &lt;number-of-bits&gt;;
 	redirect-server &lt;server-to-forward-to&gt;;
 	redirect-port &lt;port-to-forward-to&gt;;
 	options {
@@ -854,6 +855,16 @@
 <p><b>maxperip</b> (optional, but recommended)<br>
    Allows you to specify how many connections per IP are allowed to this server (ex: maxperip 4;).
 </p>
+<p id="allowblock_ipv6clonemask"><b>ipv6-clone-mask</b>
+  (optional, defaults to <a href="#set_defaultipv6clonemask">set::default-ipv6-clone-mask</a>)<br />
+  This option controls clone detection. If two clients connect from different IPv6 addresses
+  but only the last few bits are different, there is almost a guarantee that both clients
+  are really one person. This option only affects the enforcement of
+  <a href="#allowblock_maxperip">allow::maxperip</a>. For example, if you set this option to
+  128, then each IPv6 address will be considered unique. Because of current IP allocation
+  policies, it is recommended that your most general <a href="#allowblock">allow block</a>
+  use a value of 64.
+</p>
 <p><b>redirect-server</b> (optional)<br>
    If the class is full, redirect users to this server (if clients supports it [mIRC 6 does]).</p>
 <p><b>redirect-port</b> (optional)<br>
@@ -2467,6 +2478,12 @@
 <p><font class="set">set::default-server &lt;server-name&gt;;</font><br>
   Defines the name of the default server to tell users to connect to if this server 
   is full.</p>
+<p id="set_defaultipv6clonemask">
+  <font class="set">set::default-ipv6-clone-mask</font><br />
+  The default IPv6 clone detection mask. See
+  <a href="#allowblock_ipv6clonemask">allow::ipv6-clone-mask</a>. The default value for this
+  setting is 64.
+  </p>
 <p id="set-services-server"><font class="set">set::services-server &lt;server-name&gt;;</font><br>
   Specifies the name of the server that the services bots are connected to. Required, 
   set it to something like services.yournet.com if you don't have services.</p>
Index: include/dynconf.h
===================================================================
RCS file: /cvs/unreal/include/dynconf.h,v
retrieving revision 1.1.1.1.6.1.2.52.2.23
diff -u -B -r1.1.1.1.6.1.2.52.2.23 dynconf.h
--- include/dynconf.h	12 Jan 2010 18:30:35 -0000	1.1.1.1.6.1.2.52.2.23
+++ include/dynconf.h	13 Aug 2010 04:48:20 -0000
@@ -164,6 +164,9 @@
 	int watch_away_notification;
 	int uhnames;
 	aNetwork network;
+#ifdef INET6
+	unsigned short default_ipv6_clone_mask;
+#endif /* INET6 */
 };
 
 #ifndef DYNCONF_C
Index: include/h.h
===================================================================
RCS file: /cvs/unreal/include/h.h,v
retrieving revision 1.1.1.1.6.1.2.173.2.67
diff -u -B -r1.1.1.1.6.1.2.173.2.67 h.h
--- include/h.h	22 Jul 2010 12:32:06 -0000	1.1.1.1.6.1.2.173.2.67
+++ include/h.h	13 Aug 2010 04:48:20 -0000
@@ -136,6 +136,9 @@
 int			AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username);
 int parse_netmask(const char *text, struct irc_netmask *netmask);
 int match_ip(struct IN_ADDR addr, char *uhost, char *mask, struct irc_netmask *netmask);
+#ifdef INET6
+int match_ipv6(struct IN_ADDR *addr, struct IN_ADDR *mask, int bits);
+#endif
 ConfigItem_ban  *Find_ban_ip(aClient *sptr);
 extern MODVAR Link	*Servers;
 void add_ListItem(ListStruct *, ListStruct **);
Index: include/struct.h
===================================================================
RCS file: /cvs/unreal/include/struct.h,v
retrieving revision 1.1.1.1.6.1.2.261.2.52
diff -u -B -r1.1.1.1.6.1.2.261.2.52 struct.h
--- include/struct.h	16 Jun 2010 05:44:14 -0000	1.1.1.1.6.1.2.261.2.52
+++ include/struct.h	13 Aug 2010 04:48:20 -0000
@@ -1210,6 +1210,9 @@
 	ConfigItem_class	*class;
 	struct irc_netmask	*netmask;
 	ConfigFlag_allow	flags;
+#ifdef INET6
+	unsigned short ipv6_clone_mask;
+#endif /* INET6 */
 };
 
 struct _configitem_oper {
Index: src/s_conf.c
===================================================================
RCS file: /cvs/unreal/src/s_conf.c,v
retrieving revision 1.1.1.1.6.1.2.355.2.113
diff -u -B -r1.1.1.1.6.1.2.355.2.113 s_conf.c
--- src/s_conf.c	19 Jun 2010 22:00:05 -0000	1.1.1.1.6.1.2.355.2.113
+++ src/s_conf.c	13 Aug 2010 04:48:22 -0000
@@ -1725,6 +1725,9 @@
 	i->watch_away_notification = 1;
 	i->new_linking_protocol = 1;
 	i->uhnames = 1;
+#ifdef INET6
+	i->default_ipv6_clone_mask = 64;
+#endif /* INET6 */
 }
 
 /* 1: needed for set::options::allow-part-if-shunned,
@@ -2469,6 +2472,9 @@
 	ConfigCommand	*cc;
 	int		errors = 0;
 	Hook *h;
+#ifdef INET6
+	ConfigItem_allow *allow;
+#endif /* INET6 */
 	for (cfptr = conf; cfptr; cfptr = cfptr->cf_next)
 	{
 		if (config_verbose > 1)
@@ -2491,6 +2497,17 @@
 			}
 		}
 	}
+#ifdef INET6
+	/*
+	 * transfer default values from set::ipv6_clones_mask into
+	 * each individual allow block. If other similar things like
+	 * this stack up here, perhaps this shoul be moved to another
+	 * function.
+	 */
+	for(allow = conf_allow; allow; allow = (ConfigItem_allow *)allow->next)
+		if(!allow->ipv6_clone_mask)
+			allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask;
+#endif /* INET6 */
 
 	close_listeners();
 	listen_cleanup();
@@ -2884,6 +2901,10 @@
 	int  i, ii = 0;
 	static char uhost[HOSTLEN + USERLEN + 3];
 	static char fullname[HOSTLEN + 1];
+#ifdef INET6
+	short is_ipv4;
+	struct IN_ADDR ipv6_mask_addr;
+#endif /* INET6 */
 
 	for (aconf = conf_allow; aconf; aconf = (ConfigItem_allow *) aconf->next)
 	{
@@ -2958,19 +2979,45 @@
 		else
 			strncpyzt(uhost, sockhost, sizeof(uhost));
 		get_sockhost(cptr, uhost);
+#ifdef INET6
+		is_ipv4 = IN6_IS_ADDR_V4MAPPED(&cptr->ip);
+
+		if(is_ipv4)
+		{
+			/*
+			 * initialize a mask for IPv6 clones detection. It has
+			 * to be zeroed out after aconf->ipv6_clone_mask
+			 * bits. Code inspired by match_ipv6().
+			 */
+			memset(&ipv6_mask_addr, 0, sizeof(ipv6_mask_addr));
+			i = aconf->ipv6_clone_mask / 8;
+			memcpy(&ipv6_mask_addr, &cptr->ip, i);
+			/* support ipv6_clone_mask not being a multiple of 8 */
+			ipv6_mask_addr.s6_addr[i] |=
+				~((1 << (8 - aconf->ipv6_clone_mask % 8)) - 1) & cptr->ip.s6_addr[i];
+		}
+#endif /* INET6 */
+
 		/* FIXME */
 		if (aconf->maxperip)
 		{
 			ii = 1;
 			for (i = LastSlot; i >= 0; i--)
-				if (local[i] && MyClient(local[i]) &&
+				if (local[i] && MyClient(local[i])
 #ifndef INET6
-				    local[i]->ip.S_ADDR == cptr->ip.S_ADDR)
+				    && local[i]->ip.S_ADDR == cptr->ip.S_ADDR)
 #else
-				    !bcmp(local[i]->ip.S_ADDR, cptr->ip.S_ADDR, sizeof(cptr->ip.S_ADDR)))
+				    /*
+				     * match IPv4 exactly and the ipv6
+				     * based on ipv6_clone_mask.
+				     */
+				    && (is_ipv4
+					? !bcmp(local[i]->ip.S_ADDR, cptr->ip.S_ADDR, sizeof(cptr->ip.S_ADDR))
+					: match_ipv6(&local[i]->ip, &cptr->ip, aconf->ipv6_clone_mask)))
+						
 #endif
 				{
-					ii++;
+					ii ++;
 					if (ii > aconf->maxperip)
 					{
 						exit_client(cptr, cptr, &me,
@@ -4780,6 +4827,17 @@
 			allow->server = strdup(cep->ce_vardata);
 		else if (!strcmp(cep->ce_varname, "redirect-port"))
 			allow->port = atoi(cep->ce_vardata);
+		else if (!strcmp(cep->ce_varname, "ipv6-clone-mask"))
+		{
+#ifdef INET6
+			/*
+			 * If this item isn't set explicitly by the
+			 * user, the value will temporarily be
+			 * zero. Defaults are applied in config_run().
+			 */
+			allow->ipv6_clone_mask = atoi(cep->ce_vardata);
+#endif /* INET6 */
+		}
 		else if (!strcmp(cep->ce_varname, "options"))
 		{
 			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) 
@@ -4885,6 +4943,30 @@
 				errors++;
 			}
 		}
+		else if (!strcmp(cep->ce_varname, "ipv6-clone-mask"))
+		{
+			/* keep this in sync with _test_set() */
+			int ipv6mask;
+			ipv6mask = atoi(cep->ce_vardata);
+			if (ipv6mask == 0)
+			{
+				config_error("%s:%d: allow::ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.",
+					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
+				errors ++;
+			}
+			if (ipv6mask > 128)
+			{
+				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
+					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
+					     ipv6mask);
+				errors ++;
+			}
+			if (ipv6mask <= 32)
+			{
+				config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.",
+					    cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
+			}
+		}
 		else if (!strcmp(cep->ce_varname, "hostname"))
 		{
 			if (has_hostname)
@@ -7540,6 +7622,12 @@
 			}
 #endif /* USE_SSL */
 		}
+		else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask"))
+		{
+#ifdef INET6
+			tempiConf.default_ipv6_clone_mask = atoi(cep->ce_vardata);
+#endif /* INET6 */
+		}
 		else 
 		{
 			int value;
@@ -8472,7 +8560,31 @@
 				}	
 				
 			}
-#endif
+#endif /* USE_SSL */
+		}
+		else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask"))
+		{
+			/* keep this in sync with _test_allow() */
+			int ipv6mask;
+			ipv6mask = atoi(cep->ce_vardata);
+			if (ipv6mask == 0)
+			{
+				config_error("%s:%d: set::default-ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.",
+					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
+				errors ++;
+			}
+			if (ipv6mask > 128)
+			{
+				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
+					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
+					     ipv6mask);
+				errors ++;
+			}
+			if (ipv6mask <= 32)
+			{
+				config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.",
+					    cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
+			}
 		}
 		else
 		{
