Author: markt
Date: Wed May 23 14:23:01 2018
New Revision: 1832105

URL: http://svn.apache.org/viewvc?rev=1832105&view=rev
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=51497
Add an option, ipv6Canonical, to the AccessLogValve that causes IPv6 addresses 
to be output in canonical form defined by RFC 5952.
Based on a patch by ognjen.

Added:
    tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java   (with props)
    tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java   (with 
props)
Modified:
    tomcat/trunk/java/org/apache/catalina/valves/AbstractAccessLogValve.java
    tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/valve.xml

Modified: 
tomcat/trunk/java/org/apache/catalina/valves/AbstractAccessLogValve.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/AbstractAccessLogValve.java?rev=1832105&r1=1832104&r2=1832105&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/AbstractAccessLogValve.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/valves/AbstractAccessLogValve.java 
Wed May 23 14:23:01 2018
@@ -52,6 +52,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.collections.SynchronizedStack;
+import org.apache.tomcat.util.net.IPv6Utils;
 
 
 /**
@@ -174,6 +175,11 @@ public abstract class AbstractAccessLogV
      */
     protected boolean enabled = true;
 
+     /**
+     * Use IPv6 canonical representation format as defined by RFC 5952.
+     */
+    private boolean ipv6Canonical = false;
+
     /**
      * The pattern used to format our access log lines.
      */
@@ -480,6 +486,16 @@ public abstract class AbstractAccessLogV
 
     // ------------------------------------------------------------- Properties
 
+    public boolean getIpv6Canonical() {
+        return ipv6Canonical;
+    }
+
+
+    public void setIpv6Canonical(boolean ipv6Canonical) {
+        this.ipv6Canonical = ipv6Canonical;
+    }
+
+
     /**
      * {@inheritDoc}
      * Default is <code>false</code>.
@@ -489,6 +505,7 @@ public abstract class AbstractAccessLogV
         this.requestAttributesEnabled = requestAttributesEnabled;
     }
 
+
     /**
      * {@inheritDoc}
      */
@@ -792,11 +809,11 @@ public abstract class AbstractAccessLogV
     /**
      * write local IP address - %A
      */
-    protected static class LocalAddrElement implements AccessLogElement {
+    protected class LocalAddrElement implements AccessLogElement {
 
-        private static final String LOCAL_ADDR_VALUE;
+        private final String localAddrValue;
 
-        static {
+        public LocalAddrElement(boolean ipv6Canonical) {
             String init;
             try {
                 init = InetAddress.getLocalHost().getHostAddress();
@@ -804,13 +821,18 @@ public abstract class AbstractAccessLogV
                 ExceptionUtils.handleThrowable(e);
                 init = "127.0.0.1";
             }
-            LOCAL_ADDR_VALUE = init;
+
+            if (ipv6Canonical) {
+                localAddrValue = IPv6Utils.canonize(init);
+            } else {
+                localAddrValue = init;
+            }
         }
 
         @Override
         public void addElement(CharArrayWriter buf, Date date, Request request,
                 Response response, long time) {
-            buf.append(LOCAL_ADDR_VALUE);
+            buf.append(localAddrValue);
         }
     }
 
@@ -821,16 +843,22 @@ public abstract class AbstractAccessLogV
         @Override
         public void addElement(CharArrayWriter 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.toString());
+                    value = addr.toString();
                 }
             } else {
-                buf.append(request.getRemoteAddr());
+                value = request.getRemoteAddr();
+            }
+
+            if (ipv6Canonical) {
+                value = IPv6Utils.canonize(value);
             }
+            buf.append(value);
         }
     }
 
@@ -854,6 +882,10 @@ public abstract class AbstractAccessLogV
             if (value == null || value.length() == 0) {
                 value = "-";
             }
+
+            if (ipv6Canonical) {
+                value = IPv6Utils.canonize(value);
+            }
             buf.append(value);
         }
     }
@@ -1348,11 +1380,15 @@ public abstract class AbstractAccessLogV
     /**
      * write local server name - %v
      */
-    protected static class LocalServerNameElement implements AccessLogElement {
+    protected class LocalServerNameElement implements AccessLogElement {
         @Override
         public void addElement(CharArrayWriter buf, Date date, Request request,
                 Response response, long time) {
-            buf.append(request.getServerName());
+            if (ipv6Canonical) {
+                buf.append(IPv6Utils.canonize(request.getServerName()));
+            } else {
+                buf.append(request.getServerName());
+            }
         }
     }
 
@@ -1649,7 +1685,7 @@ public abstract class AbstractAccessLogV
         case 'a':
             return new RemoteAddrElement();
         case 'A':
-            return new LocalAddrElement();
+            return new LocalAddrElement(ipv6Canonical);
         case 'b':
             return new ByteSentElement(true);
         case 'B':

Modified: 
tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java?rev=1832105&r1=1832104&r2=1832105&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java 
Wed May 23 14:23:01 2018
@@ -604,7 +604,7 @@ public class ExtendedAccessLogValve exte
         } else if ("s".equals(token)) {
             String nextToken = tokenizer.getToken();
             if ("ip".equals(nextToken)) {
-                return new LocalAddrElement();
+                return new LocalAddrElement(getIpv6Canonical());
             } else if ("dns".equals(nextToken)) {
                 return new AccessLogElement() {
                     @Override

Added: tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java?rev=1832105&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java Wed May 23 
14:23:01 2018
@@ -0,0 +1,252 @@
+/*
+ *  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;
+
+/**
+ * <p>IPv6 utilities.
+ * <p>For the moment, it only contains function to canonicalize 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;
+
+    /**
+     * <p>Convert IPv6 address into RFC 5952 form.
+     * E.g. 2001:db8:0:1:0:0:0:1 -> 2001:db8:0:1::1</p>
+     *
+     * <p>Method is null safe, and if IPv4 address or host name is passed to 
the
+     * method it is returned without any processing.</p>
+     *
+     * <p>Method also supports IPv4 in IPv6 (e.g. 0:0:0:0:0:ffff:192.0.2.1 ->
+     * ::ffff:192.0.2.1), and zone ID (e.g. fe80:0:0:0:f0f0:c0c0:1919:1234%4
+     * -> fe80::f0f0:c0c0:1919:1234%4).</p>
+     *
+     * <p>The behaviour of this method is undefined if an invalid IPv6 address
+     * is passed in as input.</p>
+     *
+     * @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;
+        }
+
+        // Definitely not an IPv6, return untouched input.
+        if (!mayBeIPv6Address(ipv6Address)) {
+            return ipv6Address;
+        }
+
+        // Length without zone ID (%zone) or IPv4 address
+        int ipv6AddressLength = ipv6Address.length();
+        if (ipv6Address.contains(".")) {
+            // IPv4 in IPv6
+            // e.g. 0:0:0:0:0:FFFF:127.0.0.1
+            int lastColonPos = ipv6Address.lastIndexOf(":");
+            int lastColonsPos = ipv6Address.lastIndexOf("::");
+            if (lastColonsPos >= 0 && lastColonPos == lastColonsPos + 1) {
+                /*
+                 *  IPv6 part ends with two consecutive colons,
+                 *  last colon is part of IPv6 format.
+                 *  e.g. ::127.0.0.1
+                 */
+                ipv6AddressLength = lastColonPos + 1;
+            } else {
+                /*
+                 *  IPv6 part ends with only one colon,
+                 *  last colon is not part of IPv6 format.
+                 *  e.g. ::FFFF:127.0.0.1
+                 */
+                ipv6AddressLength = lastColonPos;
+            }
+        } else if (ipv6Address.contains("%")) {
+            // Zone ID
+            // e.g. fe80:0:0:0:f0f0:c0c0:1919:1234%4
+            ipv6AddressLength = ipv6Address.lastIndexOf("%");
+        }
+
+        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;
+        boolean groupStart = true;
+
+        /*
+         *  Two consecutive colons, initial expansion.
+         *  e.g. 2001:db8:0:0:1::1 -> 2001:db8:0:0:1:0:0:1
+         */
+
+        StringBuilder expanded = new StringBuilder(ipv6Address);
+        int colonsPos = ipv6Address.indexOf("::");
+        int length = ipv6AddressLength;
+        int change = 0;
+
+        if (colonsPos >= 0 && colonsPos < ipv6AddressLength - 2) {
+            int colonCounter = 0;
+            for (int i = 0; i < ipv6AddressLength; i++) {
+                if (ipv6Address.charAt(i) == ':') {
+                    colonCounter++;
+                }
+            }
+
+            if (colonsPos == 0) {
+                expanded.insert(0, "0");
+                change = change + 1;
+            }
+
+            for (int i = 0; i < MAX_NUMBER_OF_GROUPS - colonCounter; i++) {
+                expanded.insert(colonsPos + 1, "0:");
+                change = change + 2;
+            }
+
+
+            if (colonsPos == ipv6AddressLength - 2) {
+                expanded.setCharAt(colonsPos + change + 1, '0');
+            } else {
+                expanded.deleteCharAt(colonsPos + change + 1);
+                change = change - 1;
+            }
+            length = length + change;
+        }
+
+
+        // Processing one char at the time
+        for (int charCounter = 0; charCounter < length; charCounter++) {
+            char c = expanded.charAt(charCounter);
+            if (c >= 'A' && c <= 'F') {
+                c = (char) (c + 32);
+            }
+            if (c != ':') {
+                groups[groupCounter][charInGroupCounter] = c;
+                if (!(groupStart && c == '0')) {
+                    ++charInGroupCounter;
+                    groupStart = false;
+                }
+                if (c != '0') {
+                    isZero = false;
+                }
+            }
+            if (c == ':' || charCounter == (length - 1)) {
+                // We reached end of current group
+                if (isZero) {
+                    ++zeroGroupLength;
+                    if (zeroGroupIndex == -1) {
+                        zeroGroupIndex = groupCounter;
+                    }
+                }
+
+                if (!isZero || charCounter == (length - 1)) {
+                    // We reached end of zero group
+                    if (zeroGroupLength > maxZeroGroupLength) {
+                        maxZeroGroupLength = zeroGroupLength;
+                        maxZeroGroupIndex = zeroGroupIndex;
+                    }
+                    zeroGroupLength = 0;
+                    zeroGroupIndex = -1;
+                }
+                ++groupCounter;
+                charInGroupCounter = 0;
+                isZero = true;
+                groupStart = true;
+            }
+        }
+
+        int numberOfGroups = groupCounter;
+
+        // Output results
+        for (groupCounter = 0; groupCounter < numberOfGroups; 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 < (numberOfGroups - 1)
+                        && (groupCounter != maxZeroGroupIndex - 1
+                                || maxZeroGroupLength <= 1)) {
+                    result.append(':');
+                }
+            } else if (groupCounter == maxZeroGroupIndex) {
+                result.append("::");
+            }
+        }
+
+        // Solve problem with three colons in IPv4 in IPv6 format
+        // e.g. 0:0:0:0:0:0:127.0.0.1 -> :::127.0.0.1 -> ::127.0.0.1
+        int resultLength = result.length();
+        if (result.charAt(resultLength - 1) == ':' && ipv6AddressLength < 
ipv6Address.length()
+                && ipv6Address.charAt(ipv6AddressLength) == ':') {
+            result.delete(resultLength - 1, resultLength);
+        }
+
+        /*
+         * Append IPv4 from IPv4-in-IPv6 format or Zone ID
+         */
+        for (int i = ipv6AddressLength; i < ipv6Address.length(); i++) {
+            result.append(ipv6Address.charAt(i));
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * Heuristic check if string might be an IPv6 address.
+     *
+     * @param address Any string or null
+     * @return true, if input string contains only hex digits and at least two 
colons, before '.' or '%' character
+     */
+    static boolean mayBeIPv6Address(String input) {
+        if (input == null) {
+            return false;
+        }
+
+        int colonsCounter = 0;
+        int length = input.length();
+        for (int i = 0; i < length; i++) {
+            char c = input.charAt(i);
+            if (c == '.' || c == '%') {
+                // IPv4 in IPv6 or Zone ID detected, end of checking.
+                break;
+            }
+            if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
+                    || (c >= 'A' && c <= 'F') || c == ':')) {
+                return false;
+            } else if (c == ':') {
+                colonsCounter++;
+            }
+        }
+        if (colonsCounter < 2) {
+            return false;
+        }
+        return true;
+    }
+}

Propchange: tomcat/trunk/java/org/apache/tomcat/util/net/IPv6Utils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java?rev=1832105&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java (added)
+++ tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java Wed May 23 
14:23:01 2018
@@ -0,0 +1,141 @@
+/*
+ *  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 org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ * Mostly examples from RFC 5952
+ */
+public class IPv6UtilsTest {
+
+    @Test
+    public void testMayBeIPv6Address() {
+        Assert.assertFalse(IPv6Utils.mayBeIPv6Address(null));
+
+        Assert.assertTrue(IPv6Utils.mayBeIPv6Address("::1"));
+        Assert.assertTrue(IPv6Utils.mayBeIPv6Address("::"));
+        Assert.assertTrue(IPv6Utils.mayBeIPv6Address("2001:db8:0:0:1:0:0:1"));
+
+        Assert.assertFalse(IPv6Utils.mayBeIPv6Address(""));
+        Assert.assertFalse(IPv6Utils.mayBeIPv6Address(":1"));
+        Assert.assertFalse(IPv6Utils.mayBeIPv6Address("123.123.123.123"));
+        
Assert.assertFalse(IPv6Utils.mayBeIPv6Address("tomcat.eu.apache.org:443"));
+    }
+
+    @Test
+    public void testCanonize() {
+        Assert.assertNull(IPv6Utils.canonize(null));
+        Assert.assertEquals("", IPv6Utils.canonize(""));
+
+        // IPv4-safe
+        Assert.assertEquals("123.123.123.123", 
IPv6Utils.canonize("123.123.123.123"));
+        Assert.assertEquals("123.1.2.23", IPv6Utils.canonize("123.1.2.23"));
+
+        // Introductory RFC 5952 examples
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8:0:0:1:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:0db8:0:0:1:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8::1:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8::0:1:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:0db8::1:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8:0:0:1::1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8:0000:0:1::1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:DB8:0:0:1::1"));
+
+        // Strip leading zeros (2.1)
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:0001"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:001"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:01"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1"));
+
+        // Zero compression (2.2)
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:0:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd::1"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:0:1", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:0:1"));
+
+        Assert.assertEquals("2001:db8::1", 
IPv6Utils.canonize("2001:db8:0:0:0::1"));
+        Assert.assertEquals("2001:db8::1", 
IPv6Utils.canonize("2001:db8:0:0::1"));
+        Assert.assertEquals("2001:db8::1", 
IPv6Utils.canonize("2001:db8:0::1"));
+        Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:db8::1"));
+
+        Assert.assertEquals("2001:db8::aaaa:0:0:1", 
IPv6Utils.canonize("2001:db8::aaaa:0:0:1"));
+        Assert.assertEquals("2001:db8::aaaa:0:0:1", 
IPv6Utils.canonize("2001:db8:0:0:aaaa::1"));
+
+        // Uppercase or lowercase (2.3)
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA"));
+        Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", 
IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa"));
+
+        // Some more zero compression for localhost addresses
+        Assert.assertEquals("::1", IPv6Utils.canonize("0:0:0:0:0:0:0:1"));
+        Assert.assertEquals("::1", 
IPv6Utils.canonize("0000:0:0:0:0:0:0:0001"));
+        Assert.assertEquals("::1", IPv6Utils.canonize("00:00:0:0:00:00:0:01"));
+        Assert.assertEquals("::1", IPv6Utils.canonize("::0001"));
+        Assert.assertEquals("::1", IPv6Utils.canonize("::1"));
+
+        // IPv6 unspecified address
+        Assert.assertEquals("::", IPv6Utils.canonize("0:0:0:0:0:0:0:0"));
+        Assert.assertEquals("::", IPv6Utils.canonize("0000:0:0:0:0:0:0:0000"));
+        Assert.assertEquals("::", IPv6Utils.canonize("00:00:0:0:00:00:0:00"));
+        Assert.assertEquals("::", IPv6Utils.canonize("::0000"));
+        Assert.assertEquals("::", IPv6Utils.canonize("::0"));
+        Assert.assertEquals("::", IPv6Utils.canonize("::"));
+
+        // Leading zeros (4.1)
+        Assert.assertEquals("2001:db8::1", 
IPv6Utils.canonize("2001:0db8::0001"));
+
+        // Shorten as much as possible (4.2.1)
+        Assert.assertEquals("2001:db8::2:1", 
IPv6Utils.canonize("2001:db8:0:0:0:0:2:1"));
+        Assert.assertEquals("2001:db8::", 
IPv6Utils.canonize("2001:db8:0:0:0:0:0:0"));
+
+        // Handling One 16-Bit 0 Field (4.2.2)
+        Assert.assertEquals("2001:db8:0:1:1:1:1:1", 
IPv6Utils.canonize("2001:db8:0:1:1:1:1:1"));
+        Assert.assertEquals("2001:db8:0:1:1:1:1:1", 
IPv6Utils.canonize("2001:db8::1:1:1:1:1"));
+
+        // Choice in Placement of "::" (4.2.3)
+        Assert.assertEquals("2001:0:0:1::1", 
IPv6Utils.canonize("2001:0:0:1:0:0:0:1"));
+        Assert.assertEquals("2001:db8::1:0:0:1", 
IPv6Utils.canonize("2001:db8:0:0:1:0:0:1"));
+
+        // IPv4 inside IPv6
+        Assert.assertEquals("::ffff:192.0.2.1", 
IPv6Utils.canonize("::ffff:192.0.2.1"));
+        Assert.assertEquals("::ffff:192.0.2.1", 
IPv6Utils.canonize("0:0:0:0:0:ffff:192.0.2.1"));
+        Assert.assertEquals("::192.0.2.1", IPv6Utils.canonize("::192.0.2.1"));
+        Assert.assertEquals("::192.0.2.1", 
IPv6Utils.canonize("0:0:0:0:0:0:192.0.2.1"));
+
+        // Zone ID
+        Assert.assertEquals("fe80::f0f0:c0c0:1919:1234%4", 
IPv6Utils.canonize("fe80::f0f0:c0c0:1919:1234%4"));
+        Assert.assertEquals("fe80::f0f0:c0c0:1919:1234%4", 
IPv6Utils.canonize("fe80:0:0:0:f0f0:c0c0:1919:1234%4"));
+
+        Assert.assertEquals("::%4", IPv6Utils.canonize("::%4"));
+        Assert.assertEquals("::%4", IPv6Utils.canonize("::0%4"));
+        Assert.assertEquals("::%4", IPv6Utils.canonize("0:0::0%4"));
+        Assert.assertEquals("::%4", IPv6Utils.canonize("0:0:0:0:0:0:0:0%4"));
+
+        Assert.assertEquals("::1%4", IPv6Utils.canonize("::1%4"));
+        Assert.assertEquals("::1%4", IPv6Utils.canonize("0:0::1%4"));
+        Assert.assertEquals("::1%4", IPv6Utils.canonize("0:0:0:0:0:0:0:1%4"));
+
+        Assert.assertEquals("::1%eth0", IPv6Utils.canonize("::1%eth0"));
+        Assert.assertEquals("::1%eth0", IPv6Utils.canonize("0:0::1%eth0"));
+        Assert.assertEquals("::1%eth0", 
IPv6Utils.canonize("0:0:0:0:0:0:0:1%eth0"));
+
+        // Hostname safety
+        Assert.assertEquals("www.apache.org", 
IPv6Utils.canonize("www.apache.org"));
+        Assert.assertEquals("ipv6.google.com", 
IPv6Utils.canonize("ipv6.google.com"));
+    }
+}

Propchange: tomcat/trunk/test/org/apache/tomcat/util/net/IPv6UtilsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1832105&r1=1832104&r2=1832105&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Wed May 23 14:23:01 2018
@@ -92,6 +92,11 @@
         usually performed during web application stop if that stop is triggered
         by a JVM shutdown. (markt)
       </fix>
+      <add>
+        <bug>51497</bug>: Add an option, <code>ipv6Canonical</code>, to the
+        <code>AccessLogValve</code> that causes IPv6 addresses to be output in
+        canonical form defined by RFC 5952. (ognjen/markt)
+      </add>
       <fix>
         <bug>62343</bug>: Make CORS filter defaults more secure. This is the 
fix
         for CVE-2018-8014. (markt)

Modified: tomcat/trunk/webapps/docs/config/valve.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/valve.xml?rev=1832105&r1=1832104&r2=1832105&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/valve.xml (original)
+++ tomcat/trunk/webapps/docs/config/valve.xml Wed May 23 14:23:01 2018
@@ -181,6 +181,16 @@
         </p>
       </attribute>
 
+      <attribute name="ipv6Canonical" 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>false</code>
+        </p>
+      </attribute>
+
       <attribute name="locale" required="false">
         <p>The locale used to format timestamps in the access log
            lines. Any timestamps configured using an



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to