fix long-standing bug with regard to protocol-on-demand-loading and iptables-save/restore (1.2.7 release bug)
diff --git a/iptables.c b/iptables.c
index cd3c1fe..fa570c5 100644
--- a/iptables.c
+++ b/iptables.c
@@ -1659,6 +1659,7 @@
 	const char *jumpto = "";
 	char *protocol = NULL;
 	const char *modprobe = NULL;
+	int proto_used = 0;
 
 	memset(&fw, 0, sizeof(fw));
 
@@ -1686,7 +1687,7 @@
 	opterr = 0;
 
 	while ((c = getopt_long(argc, argv,
-	   "-A:C:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
+	   "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
 					   opts, NULL)) != -1) {
 		switch (c) {
 			/*
@@ -1839,20 +1840,6 @@
 				exit_error(PARAMETER_PROBLEM,
 					   "rule would never match protocol");
 			fw.nfcache |= NFC_IP_PROTO;
-
-			/* try to load match with protocol name */
-			if ((m = find_proto(protocol, TRY_LOAD,
-					    options&OPT_NUMERIC))) {
-				size_t size;
-				size = IPT_ALIGN(sizeof(struct ipt_entry_match))
-						 + m->size;
-				m->m = fw_calloc(1, size);
-				m->m->u.match_size = size;
-				strcpy(m->m->u.user.name, m->name);
-				m->init(m->m, &fw.nfcache);
-				opts = merge_options(opts, m->extra_opts, &m->option_offset);
-			}
-
 			break;
 
 		case 's':
@@ -2038,6 +2025,59 @@
 						     &m->m))
 						break;
 				}
+
+				/* If you listen carefully, you can
+				   actually hear this code suck. */
+
+				/* some explanations (after four different bugs
+				 * in 3 different releases): If we encountere a
+				 * parameter, that has not been parsed yet,
+				 * it's not an option of an explicitly loaded
+				 * match or a target.  However, we support
+				 * implicit loading of the protocol match
+				 * extension.  '-p tcp' means 'l4 proto 6' and
+				 * at the same time 'load tcp protocol match on
+				 * demand if we specify --dport'.
+				 *
+				 * To make this work, we need to make sure:
+				 * - the parameter has not been parsed by
+				 *   a match (m above)
+				 * - a protocol has been specified
+				 * - the protocol extension has not been
+				 *   loaded yet, or is loaded and unused
+				 *   [think of iptables-restore!]
+				 * - the protocol extension can be successively
+				 *   loaded
+				 */
+				if (m == NULL
+				    && protocol
+				    && (!find_proto(protocol, DONT_LOAD,
+						   options&OPT_NUMERIC) 
+					|| (find_proto(protocol, DONT_LOAD,
+							options&OPT_NUMERIC)
+					    && (proto_used == 0))
+				       )
+				    && (m = find_proto(protocol, TRY_LOAD,
+						       options&OPT_NUMERIC))) {
+					/* Try loading protocol */
+					size_t size;
+					
+					proto_used = 1;
+
+					size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+							 + m->size;
+
+					m->m = fw_calloc(1, size);
+					m->m->u.match_size = size;
+					strcpy(m->m->u.user.name, m->name);
+					m->init(m->m, &fw.nfcache);
+
+					opts = merge_options(opts,
+					    m->extra_opts, &m->option_offset);
+
+					optind--;
+					continue;
+				}
 				if (!m)
 					exit_error(PARAMETER_PROBLEM,
 						   "Unknown arg `%s'",