Correct various edge cases in the new HTTP Host header validation parser.
Patch provided by Katya Todorova.
Fix IPv6/IPv4 parsing for host header:
- chars other than : should not be allowed in IPv6 address after ]
- ::: should not present in IPv6 address
- IPv4 part of IPv6 address was not correctly parsed (1 symbol of IPv4 part was ignored)
- tests added to cover IPv4/6 parsing
- parsed test class fixed not to throw NPE when an exception is expected but not thrown
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk@1830251 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java
index 62687fa..a945493 100644
--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
+++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
@@ -577,45 +577,50 @@
int h16Size = 0;
int pos = 1;
boolean parsedDoubleColon = false;
- boolean previousWasColon = false;
+ int precedingColonsCount = 0;
do {
c = reader.read();
- if (h16Count == 0 && previousWasColon && c != ':') {
+ if (h16Count == 0 && precedingColonsCount == 1 && c != ':') {
// Can't start with a single :
throw new IllegalArgumentException();
}
if (HttpParser.isHex(c)) {
if (h16Size == 0) {
// Start of a new h16 block
- previousWasColon = false;
+ precedingColonsCount = 0;
h16Count++;
- reader.mark(4);
}
h16Size++;
if (h16Size > 4) {
throw new IllegalArgumentException();
}
} else if (c == ':') {
- if (previousWasColon) {
- // End of ::
- if (parsedDoubleColon) {
- // Only allowed one :: sequence
- throw new IllegalArgumentException();
- }
- parsedDoubleColon = true;
- previousWasColon = false;
- // :: represents at least one h16 block
- h16Count++;
+ if (precedingColonsCount >=2 ) {
+ // ::: is not allowed
+ throw new IllegalArgumentException();
} else {
- previousWasColon = true;
+ if(precedingColonsCount == 1) {
+ // End of ::
+ if (parsedDoubleColon ) {
+ // Only allowed one :: sequence
+ throw new IllegalArgumentException();
+ }
+ parsedDoubleColon = true;
+ // :: represents at least one h16 block
+ h16Count++;
+ }
+ precedingColonsCount++;
+ // mark if the next symbol is hex before the actual read
+ reader.mark(4);
}
h16Size = 0;
} else if (c == ']') {
- if (previousWasColon) {
+ if (precedingColonsCount == 1) {
// Can't end on a single ':'
throw new IllegalArgumentException();
}
+ pos++;
break;
} else if (c == '.') {
if (h16Count == 7 || h16Count < 7 && parsedDoubleColon) {
@@ -641,9 +646,12 @@
c = reader.read();
if (c == ':') {
- return pos + 1;
+ return pos;
} else {
- return -1;
+ if(c == -1) {
+ return -1;
+ }
+ throw new IllegalArgumentException();
}
}
diff --git a/res/maven/mvn-pub.xml b/res/maven/mvn-pub.xml
index 1bd7454..b8504a5 100644
--- a/res/maven/mvn-pub.xml
+++ b/res/maven/mvn-pub.xml
@@ -49,35 +49,18 @@
</copy>
<!--sign the jar, the source and the pom -->
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="${file}"/>
- </exec>
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="${src}"/>
- </exec>
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="-o"/>
- <arg value="${pom}.asc"/>
- <arg value="${pom}.tmp"/>
- </exec>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{file}" />
+ <param name="file.out" value="@{file}.asc" />
+ </antcall>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{src}" />
+ <param name="file.out" value="@{src}.asc" />
+ </antcall>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{pom}.tmp" />
+ <param name="file.out" value="@{pom}.asc" />
+ </antcall>
<artifact:deploy file="${file}">
<pom file="${pom}.tmp"/>
@@ -131,26 +114,14 @@
</copy>
<!--sign the file and pom -->
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="${file}"/>
- </exec>
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="-o"/>
- <arg value="${pom}.asc"/>
- <arg value="${pom}.tmp"/>
- </exec>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{file}" />
+ <param name="file.out" value="@{file}.asc" />
+ </antcall>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{pom}.tmp" />
+ <param name="file.out" value="@{pom}.asc" />
+ </antcall>
<artifact:deploy file="${file}">
<pom file="${pom}.tmp"/>
@@ -198,35 +169,18 @@
</copy>
<!--sign the zip, the tar.gz and the pom -->
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="${file}.zip"/>
- </exec>
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="${file}.tar.gz"/>
- </exec>
- <exec executable="${gpg.exec}" failonerror="true"
- inputstring="${gpg.passphrase}">
- <arg value="--batch"/>
- <arg value="--passphrase-fd"/>
- <arg value="0"/>
- <arg value="-a"/>
- <arg value="-b"/>
- <arg value="-o"/>
- <arg value="${pom}.asc"/>
- <arg value="${pom}.tmp"/>
- </exec>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{file}" />
+ <param name="file.out" value="@{file}.asc" />
+ </antcall>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{file}.tar.gz" />
+ <param name="file.out" value="@{file}.tar.gz.asc" />
+ </antcall>
+ <antcall target="-sign" >
+ <param name="file.in" value="@{pom}.tmp" />
+ <param name="file.out" value="@{pom}.asc" />
+ </antcall>
<artifact:deploy file="${pom}">
<pom file="${pom}.tmp"/>
@@ -262,7 +216,7 @@
</sequential>
</macrodef>
- <target name="generic-deploy" depends="init-maven,init-gpg,init-ldap">
+ <target name="generic-deploy" depends="init-maven,init-gpg-1,init-gpg-2,init-ldap">
<!-- Standard jars in bin directory -->
<!-- Skip bootstrap.jar - it is just a subset of catalina.jar -->
<doMavenDeploy artifactId="tomcat-juli"
@@ -413,7 +367,11 @@
</antcall>
</target>
- <target name="init-gpg">
+ <target name="init-gpg-1">
+ <available file="${gpg.exec}" property="gpg.exec.available"/>
+ </target>
+
+ <target name="init-gpg-2" if="${gpg.exec.available}">
<input message="Enter GPG pass-phrase" addproperty="gpg.passphrase" >
<handler type="secure"/>
</input>
@@ -426,4 +384,19 @@
</input>
</target>
+ <target name="-sign" if="gpg.passphrase">
+ <fail unless="file" />
+ <exec executable="${gpg.exec}" failonerror="true"
+ inputstring="${gpg.passphrase}">
+ <arg value="--batch"/>
+ <arg value="--passphrase-fd"/>
+ <arg value="0"/>
+ <arg value="-a"/>
+ <arg value="-b"/>
+ <arg value="-o"/>
+ <arg value="${file.out}"/>
+ <arg value="${file.in}"/>
+ </exec>
+ </target>
+
</project>
diff --git a/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java b/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java
index f49f4bb..a6f98ba 100644
--- a/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java
+++ b/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java
@@ -66,6 +66,7 @@
result.add(new Object[] { TestType.IPv4, "0.0.0.256", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "0.a.0.0", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "0..0.0", Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv4, "0]", Integer.valueOf(-1), IAE} );
// Domain Name - valid
result.add(new Object[] { TestType.DOMAIN_NAME, "localhost", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.DOMAIN_NAME, "localhost:8080", Integer.valueOf(9), null} );
@@ -121,6 +122,7 @@
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]:8080",
Integer.valueOf(23), null} );
+ result.add(new Object[] { TestType.IPv6, "[::1.2.3.4]", Integer.valueOf(-1), null} );
// IPv6 - invalid
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:127.0.0.1]",
Integer.valueOf(-1), IAE} );
@@ -136,6 +138,18 @@
result.add(new Object[] { TestType.IPv6, "[0::0::127.0.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[0:0:G:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[00000:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[::1]'", Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[:2222:3333:4444:5555:6666:7777:8888]",
+ Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[1111:::3333:4444:5555:6666:7777:8888]",
+ Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "::1]", Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[1111:2222:3333:4444:5555:6666:7777:8888:9999]",
+ Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[1111:2222:3333:4444:5555:6666:7777:1.2.3.4]",
+ Integer.valueOf(-1), IAE} );
+ result.add(new Object[] { TestType.IPv6, "[1111:2222:3333]",
+ Integer.valueOf(-1), IAE} );
return result;
}
@@ -165,6 +179,7 @@
if (expectedException == null) {
Assert.assertNull(input, exceptionClass);
} else {
+ Assert.assertNotNull(exceptionClass);
Assert.assertTrue(input, expectedException.isAssignableFrom(exceptionClass));
}
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6cc9ac6..40f60d3 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -64,7 +64,7 @@
when the Valve is configured on a Host or an Engine. (fschumacher)
</fix>
</changelog>
- </subsection>
+ </subsection>
<subsection name="Jasper">
<changelog>
<fix>