ASF Bugzilla – Attachment 27312 Details for
Bug 51497
Use canonical IPv6 text representation in logs
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Adds RFC 5952 canonical IPv6 representation to AccessLogValve.
tomcat-ipv6-canonical.patch (text/plain), 18.13 KB, created by
Ognjen Blagojevic
on 2011-07-25 14:11:54 UTC
(
hide
)
Description:
Adds RFC 5952 canonical IPv6 representation to AccessLogValve.
Filename:
MIME Type:
Creator:
Ognjen Blagojevic
Created:
2011-07-25 14:11:54 UTC
Size:
18.13 KB
patch
obsolete
>Index: java/org/apache/catalina/valves/AccessLogValve.java >=================================================================== >--- java/org/apache/catalina/valves/AccessLogValve.java (revision 1150602) >+++ java/org/apache/catalina/valves/AccessLogValve.java (working copy) >@@ -53,6 +53,7 @@ > import org.apache.juli.logging.LogFactory; > import org.apache.tomcat.util.ExceptionUtils; > import org.apache.tomcat.util.buf.B2CConverter; >+import org.apache.tomcat.util.net.IPv6Utils; > > > /** >@@ -222,6 +223,12 @@ > > > /** >+ * Use IPv6 canonical representation format as defined by RFC 5952. >+ */ >+ protected boolean canonical = true; >+ >+ >+ /** > * The PrintWriter to which we are currently logging, if any. > */ > protected PrintWriter writer = null; >@@ -232,8 +239,8 @@ > * "yyyy-MM-dd". > */ > protected SimpleDateFormat fileDateFormatter = null; >+ > >- > /** > * The system timezone. > */ >@@ -748,6 +755,24 @@ > > > /** >+ * Are IPv6 addresses represented in canonical representation format >+ */ >+ public boolean isCanonical() { >+ return canonical; >+ } >+ >+ >+ /** >+ * Set the value if IPv6 addresses should be represented in canonical representation format? >+ * >+ * @param canonical true if canonical. >+ */ >+ public void setCanonical(boolean canonical) { >+ this.canonical = canonical; >+ } >+ >+ >+ /** > * Set the resolve hosts flag. > * > * @param resolveHosts The new resolve hosts value >@@ -1217,11 +1242,11 @@ > /** > * write local IP address - %A > */ >- protected static class LocalAddrElement implements AccessLogElement { >+ protected class LocalAddrElement implements AccessLogElement { > >- private static final String LOCAL_ADDR_VALUE; >+ private String localAddrValue; > >- static { >+ private void lazyInit() { > String init; > try { > init = InetAddress.getLocalHost().getHostAddress(); >@@ -1229,13 +1254,20 @@ > ExceptionUtils.handleThrowable(e); > init = "127.0.0.1"; > } >- LOCAL_ADDR_VALUE = init; >+ if (canonical) { >+ localAddrValue = IPv6Utils.canonize(init); >+ } else { >+ localAddrValue = init; >+ } > } > > @Override > public void addElement(StringBuilder buf, Date date, Request request, > Response response, long time) { >- buf.append(LOCAL_ADDR_VALUE); >+ if (localAddrValue == null) { >+ lazyInit(); >+ } >+ buf.append(localAddrValue); > } > } > >@@ -1246,16 +1278,22 @@ > @Override > public void addElement(StringBuilder buf, Date date, Request request, > Response response, long time) { >+ String value = null; > if (requestAttributesEnabled) { > Object addr = request.getAttribute(REMOTE_ADDR_ATTRIBUTE); > if (addr == null) { >- buf.append(request.getRemoteAddr()); >+ value = request.getRemoteAddr(); > } else { >- buf.append(addr); >+ value = addr.toString(); > } > } else { >- buf.append(request.getRemoteAddr()); >+ value = request.getRemoteAddr(); > } >+ >+ if (canonical) { >+ value = IPv6Utils.canonize(value); >+ } >+ buf.append(value); > } > } > >@@ -1279,6 +1317,9 @@ > if (value == null || value.length() == 0) { > value = "-"; > } >+ if (canonical) { >+ value = IPv6Utils.canonize(value); >+ } > buf.append(value); > } > } >@@ -1691,11 +1732,15 @@ > /** > * write local server name - %v > */ >- protected static class LocalServerNameElement implements AccessLogElement { >+ protected class LocalServerNameElement implements AccessLogElement { > @Override > public void addElement(StringBuilder buf, Date date, Request request, > Response response, long time) { >- buf.append(request.getServerName()); >+ if (canonical) { >+ buf.append(IPv6Utils.canonize(request.getServerName())); >+ } else { >+ buf.append(request.getServerName()); >+ } > } > } > >Index: java/org/apache/tomcat/util/net/IPv6Utils.java >=================================================================== >--- java/org/apache/tomcat/util/net/IPv6Utils.java (revision 0) >+++ java/org/apache/tomcat/util/net/IPv6Utils.java (revision 0) >@@ -0,0 +1,165 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You under the Apache License, Version 2.0 >+ * (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+package org.apache.tomcat.util.net; >+ >+import java.net.Inet6Address; >+import java.net.InetAddress; >+import java.net.UnknownHostException; >+ >+/** >+ * <p>IPv6 utilities. >+ * <p>For the moment, it only contains function to canonize IPv6 address into >+ * RFC 5952 form. >+ */ >+public class IPv6Utils { >+ >+ private static final int MAX_NUMBER_OF_GROUPS = 8; >+ private static final int MAX_GROUP_LENGTH = 4; >+ >+ /** >+ * Convert IPv6 adress into RFC 5952 form. >+ * (e.g. 2001:db8:0:1:0:0:0:1 -> 2001:db8:0:1::1). >+ * Function is null safe, and if IPv4 address or host name is passed to the >+ * function it is returned wihout any processing. >+ * >+ * @param ipv6Address String representing valid IPv6 address. >+ * @return String representing IPv6 in canonical form. >+ * @throws IllegalArgumentException if IPv6 format is unacceptable. >+ */ >+ public static String canonize(String ipv6Address) >+ throws IllegalArgumentException { >+ >+ if (ipv6Address == null) { >+ return null; >+ } >+ >+ // Definitly not an IPv6, return untouched input. >+ if (!mayBeIPv6Address(ipv6Address)) { >+ return ipv6Address; >+ } >+ >+ StringBuilder result = new StringBuilder(); >+ char [][] groups = new char[MAX_NUMBER_OF_GROUPS][MAX_GROUP_LENGTH]; >+ int groupCounter = 0; >+ int charInGroupCounter = 0; >+ >+ // Index of the current zeroGroup, -1 means not found. >+ int zeroGroupIndex = -1; >+ int zeroGroupLength = 0; >+ >+ // maximum length zero group, if there is more then one, then first one >+ int maxZeroGroupIndex = -1; >+ int maxZeroGroupLength = 0; >+ >+ boolean isZero = true; >+ String expanded = null; >+ try { >+ InetAddress inetAddress = InetAddress.getByName(ipv6Address); >+ if (inetAddress instanceof Inet6Address) { >+ expanded = ((Inet6Address) inetAddress).getHostAddress(); >+ } else { >+ // Probably IPv4 address, leave it as it is >+ return ipv6Address; >+ } >+ } catch (UnknownHostException uhe) { >+ // Malformed input >+ throw new IllegalArgumentException(uhe); >+ } >+ >+ int len = expanded.length(); >+ >+ // Processing one char at the time >+ for (int charCounter = 0; charCounter < len; charCounter++) { >+ char c = expanded.charAt(charCounter); >+ //System.out.println(c); >+ if (c != ':') { >+ groups[groupCounter][charInGroupCounter] = c; >+ ++charInGroupCounter; >+ if (c != '0') { >+ isZero = false; >+ } >+ } >+ if (c == ':' || charCounter == (len - 1)) { >+ // We reached end of current group >+ if (isZero && zeroGroupIndex == -1) { >+ zeroGroupIndex = groupCounter; >+ zeroGroupLength = 1; >+ } else if (!isZero) { >+ // We reached end of zero group >+ if (zeroGroupLength > maxZeroGroupLength) { >+ maxZeroGroupLength = zeroGroupLength; >+ maxZeroGroupIndex = zeroGroupIndex; >+ } >+ zeroGroupLength = 0; >+ zeroGroupIndex = -1; >+ } else { >+ ++zeroGroupLength; >+ } >+ ++groupCounter; >+ charInGroupCounter = 0; >+ isZero = true; >+ } >+ } >+ >+ // Output results >+ for (groupCounter = 0; groupCounter < MAX_NUMBER_OF_GROUPS; groupCounter++) { >+ if (maxZeroGroupLength <= 1 || groupCounter < maxZeroGroupIndex >+ || groupCounter >= maxZeroGroupIndex + maxZeroGroupLength) { >+ for (int j = 0; j < MAX_GROUP_LENGTH; j++) { >+ if (groups[groupCounter][j] != 0) { >+ result.append(groups[groupCounter][j]); >+ } >+ } >+ if (groupCounter < (MAX_NUMBER_OF_GROUPS - 1) >+ && (groupCounter != maxZeroGroupIndex - 1 >+ || maxZeroGroupLength <= 1)) { >+ result.append(':'); >+ } >+ } else if (groupCounter == maxZeroGroupIndex) { >+ result.append("::"); >+ } >+ } >+ >+ return result.toString(); >+ } >+ >+ /** >+ * Heuristic check if string might be an IPv6 address. >+ * >+ * @param address Any string or null >+ * @return true, if string contains only hex digits and at least two colons. >+ */ >+ private static boolean mayBeIPv6Address(String input) { >+ if (input == null) { >+ return false; >+ } >+ boolean result = true; >+ int colonsCounter = 0; >+ int length = input.length(); >+ for (int i = 0; i < length; i++) { >+ char c = input.charAt(i); >+ if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') >+ || (c >= 'A' && c <= 'F') || c == ':')) { >+ result = false; >+ } else if (c == ':') { >+ colonsCounter++; >+ } >+ } >+ return result; >+ >+ } >+} >Index: test/org/apache/tomcat/util/net/IPv6UtilsTest.java >=================================================================== >--- test/org/apache/tomcat/util/net/IPv6UtilsTest.java (revision 0) >+++ test/org/apache/tomcat/util/net/IPv6UtilsTest.java (revision 0) >@@ -0,0 +1,126 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You under the Apache License, Version 2.0 >+ * (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.tomcat.util.net; >+ >+import junit.framework.TestCase; >+ >+/** >+ * Mostly examples from RFC 5952 >+ */ >+public class IPv6UtilsTest extends TestCase { >+ >+ public void testCanonize() { >+ assertTrue(IPv6Utils.canonize(null) == null); >+ assertTrue(IPv6Utils.canonize("").equals("")); >+ >+ // IPv4-safe >+ assertTrue(IPv6Utils.canonize("123.123.123.123").equals( >+ "123.123.123.123")); >+ assertTrue(IPv6Utils.canonize("123.1.2.23").equals("123.1.2.23")); >+ >+ // Introductory RFC 5952 examples >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:0db8:0:0:1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8::1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8::0:1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:0db8::1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:1::1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0000:0:1::1").equals( >+ "2001:db8::1:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:DB8:0:0:1::1").equals( >+ "2001:db8::1:0:0:1")); >+ >+ // Strip leading zeros (2.1) >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:0001") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:001") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:01") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1")); >+ >+ // Zero compression (2.2) >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd::1") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:0:1") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:0:1")); >+ >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:0::1") >+ .equals("2001:db8::1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0::1").equals("2001:db8::1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0::1").equals("2001:db8::1")); >+ assertTrue(IPv6Utils.canonize("2001:db8::1").equals("2001:db8::1")); >+ >+ assertTrue(IPv6Utils.canonize("2001:db8::aaaa:0:0:1").equals( >+ "2001:db8::aaaa:0:0:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:aaaa::1").equals( >+ "2001:db8::aaaa:0:0:1")); >+ >+ // Uppercase or lowercase (2.3) >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa")); >+ assertTrue(IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa") >+ .equals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa")); >+ >+ // Some more zero compression for localhost addresses >+ assertTrue(IPv6Utils.canonize("0:0:0:0:0:0:0:1").equals("::1")); >+ assertTrue(IPv6Utils.canonize("0000:0:0:0:0:0:0:0001").equals("::1")); >+ assertTrue(IPv6Utils.canonize("00:00:0:0:00:00:0:01").equals("::1")); >+ assertTrue(IPv6Utils.canonize("::0001").equals("::1")); >+ assertTrue(IPv6Utils.canonize("::1").equals("::1")); >+ >+ // Leading zeros (4.1) >+ assertTrue(IPv6Utils.canonize("2001:0db8::0001").equals("2001:db8::1")); >+ >+ // Shorten as much as possible (4.2.1) >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:0:0:2:1").equals( >+ "2001:db8::2:1")); >+ >+ // Handling One 16-Bit 0 Field (4.2.2) >+ assertTrue(IPv6Utils.canonize("2001:db8:0:1:1:1:1:1").equals( >+ "2001:db8:0:1:1:1:1:1")); >+ assertTrue(IPv6Utils.canonize("2001:db8::1:1:1:1:1").equals( >+ "2001:db8:0:1:1:1:1:1")); >+ >+ // Choice in Placement of "::" (4.2.3) >+ assertTrue(IPv6Utils.canonize("2001:0:0:1:0:0:0:1").equals( >+ "2001:0:0:1::1")); >+ assertTrue(IPv6Utils.canonize("2001:db8:0:0:1:0:0:1").equals( >+ "2001:db8::1:0:0:1")); >+ >+ // IPv4 inside IPv6 >+ assertTrue(IPv6Utils.canonize("::ffff:192.0.2.1").equals( >+ "::ffff:192.0.2.1")); >+ >+ // Hostname safety >+ assertTrue(IPv6Utils.canonize("www.apache.org").equals( >+ "www.apache.org")); >+ assertTrue(IPv6Utils.canonize("ipv6.google.com").equals( >+ "ipv6.google.com")); >+ >+ } >+} >\ No newline at end of file >Index: webapps/docs/config/valve.xml >=================================================================== >--- webapps/docs/config/valve.xml (revision 1150602) >+++ webapps/docs/config/valve.xml (working copy) >@@ -193,6 +193,16 @@ > </p> > </attribute> > >+ <attribute name="canonical" required="false"> >+ <p>Flag to determine if IPv6 addresses should be represented in canonical >+ representation format as defined by RFC 5952. If set to <code>true</code>, >+ then IPv6 addresses will be written in canonical format (e.g. >+ <code>2001:db8::1:0:0:1</code>, <code>::1</code>), otherwise it will be >+ represented in full form (e.g. <code>2001:db8:0:0:1:0:0:1</code>, >+ <code>0:0:0:0:0:0:0:1</code>). Default value: <code>true</code> >+ </p> >+ </attribute> >+ > </attributes> > > <p>Values for the <code>pattern</code> attribute are made up of literal
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 51497
:
27312
|
29921
|
29924
|
31300