2 * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.model.ietf.util;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Verify;
12 import java.util.Arrays;
13 import javax.annotation.Nonnull;
16 * IPv6 address parsing for ietf-inet-types ipv6-address and ipv6-prefix. This is an internal implementation
17 * class, not meant to be exposed in any shape or form to the outside world, as the code relies on the fact that
18 * the strings presented to it have been previously validated to conform to the regular expressions defined in
22 * v6 routines added by Anton Ivanov on 14.6.2015
23 * revised by Robert Varga
26 * Read all of the following before you touch any v6 code or decide to
27 * optimize it by invoking a "simple" Guava call
29 * Java IPv6 is fundamentally broken and Google libraries do not fix it.
30 * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
31 * and there is absolutely no way to override this behaviour
32 * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
33 * exception. Even if they did, they re-use the same broken java code
36 * This is why we have to parse v6 by ourselves.
38 * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
40 * The original BSD code is licensed under standard BSD license. While we
41 * are not obliged to provide an attribution, credit where credit is due.
42 * As far as why it is similar to Sun's sun.net.util please ask Sun why
43 * their code has the same variable names, comments and code flow.
45 final class Ipv6Utils {
46 private static final int INADDR4SZ = 4;
47 private static final int INADDR6SZ = 16;
48 private static final int INT16SZ = Short.BYTES;
51 throw new UnsupportedOperationException();
55 * Convert Ipv6Address object to a valid Canonical v6 address in byte format
57 * @param bytes Byte array for output
58 * @param str String representation
59 * @param strLimit String offset which should not be processed
60 * @throws NullPointerException if ipv6address is null
62 static void fillIpv6Bytes(final @Nonnull byte[] bytes, final String str, final int strLimit) {
63 // Leading :: requires some special handling.
65 if (str.charAt(i) == ':') {
66 // Note ++i side-effect in check
67 Preconditions.checkArgument(str.charAt(++i) == ':', "Invalid v6 address '%s'", str);
70 boolean haveVal = false;
75 while (i < strLimit) {
76 final char ch = str.charAt(i++);
82 // removed overrun check - the regexp checks for valid data
83 bytes[j++] = (byte) ((val >>> 8) & 0xff);
84 bytes[j++] = (byte) (val & 0xff);
88 // no need to check separator position validity - regexp does that
95 // frankenstein - v4 attached to v6, mixed notation
96 if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
98 * This has passed the regexp so it is fairly safe to parse it
99 * straight away. Use the Ipv4Utils for that.
101 Ipv4Utils.fillIpv4Bytes(bytes, j, str, curtok, strLimit);
108 * Business as usual - ipv6 address digit.
109 * We can remove all checks from the original BSD code because
110 * the regexp has already verified that we are not being fed
111 * anything bigger than 0xffff between the separators.
113 final int chval = AbstractIetfYangUtil.hexValue(ch);
114 val = (val << 4) | chval;
119 Verify.verify(j + INT16SZ <= INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
120 bytes[j++] = (byte) ((val >> 8) & 0xff);
121 bytes[j++] = (byte) (val & 0xff);
125 Verify.verify(j != INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
126 expandZeros(bytes, colonp, j);
128 Verify.verify(j == INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
132 private static void expandZeros(final byte[] bytes, final int where, final int filledBytes) {
133 final int tailLength = filledBytes - where;
134 final int tailOffset = INADDR6SZ - tailLength;
135 System.arraycopy(bytes, where, bytes, tailOffset, tailLength);
136 Arrays.fill(bytes, where, tailOffset, (byte)0);