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 static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
13 import java.util.Arrays;
14 import org.eclipse.jdt.annotation.NonNull;
17 * IPv6 address parsing for ietf-inet-types ipv6-address and ipv6-prefix. This is an internal implementation
18 * class, not meant to be exposed in any shape or form to the outside world, as the code relies on the fact that
19 * the strings presented to it have been previously validated to conform to the regular expressions defined in
23 * v6 routines added by Anton Ivanov on 14.6.2015
24 * revised by Robert Varga
27 * Read all of the following before you touch any v6 code or decide to
28 * optimize it by invoking a "simple" Guava call
30 * Java IPv6 is fundamentally broken and Google libraries do not fix it.
31 * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
32 * and there is absolutely no way to override this behaviour
33 * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
34 * exception. Even if they did, they re-use the same broken java code
37 * This is why we have to parse v6 by ourselves.
39 * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
41 * The original BSD code is licensed under standard BSD license. While we
42 * are not obliged to provide an attribution, credit where credit is due.
43 * As far as why it is similar to Sun's sun.net.util please ask Sun why
44 * their code has the same variable names, comments and code flow.
46 final class Ipv6Utils {
47 private static final int INADDR4SZ = 4;
48 private static final int INADDR6SZ = 16;
49 private static final int INT16SZ = Short.BYTES;
52 throw new UnsupportedOperationException();
56 * Convert Ipv6Address object to a valid Canonical v6 address in byte format
58 * @param bytes Byte array for output
59 * @param str String representation
60 * @param strLimit String offset which should not be processed
61 * @throws NullPointerException if ipv6address is null
63 static void fillIpv6Bytes(final byte @NonNull[] bytes, final String str, final int strLimit) {
64 // Leading :: requires some special handling.
66 if (str.charAt(i) == ':') {
67 // Note ++i side-effect in check
68 checkArgument(str.charAt(++i) == ':', "Invalid v6 address '%s'", str);
71 boolean haveVal = false;
76 while (i < strLimit) {
77 final char ch = str.charAt(i++);
83 // removed overrun check - the regexp checks for valid data
84 bytes[j++] = (byte) (val >>> 8 & 0xff);
85 bytes[j++] = (byte) (val & 0xff);
89 // no need to check separator position validity - regexp does that
96 // frankenstein - v4 attached to v6, mixed notation
97 if (ch == '.' && j + INADDR4SZ <= INADDR6SZ) {
99 * This has passed the regexp so it is fairly safe to parse it
100 * straight away. Use the Ipv4Utils for that.
102 Ipv4Utils.fillIpv4Bytes(bytes, j, str, curtok, strLimit);
109 * Business as usual - ipv6 address digit.
110 * We can remove all checks from the original BSD code because
111 * the regexp has already verified that we are not being fed
112 * anything bigger than 0xffff between the separators.
114 final int chval = AbstractIetfYangUtil.hexValue(ch);
115 val = val << 4 | chval;
120 verify(j + INT16SZ <= INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
121 bytes[j++] = (byte) (val >> 8 & 0xff);
122 bytes[j++] = (byte) (val & 0xff);
126 verify(j != INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
127 expandZeros(bytes, colonp, j);
129 verify(j == INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
133 private static void expandZeros(final byte[] bytes, final int where, final int filledBytes) {
134 final int tailLength = filledBytes - where;
135 final int tailOffset = INADDR6SZ - tailLength;
136 System.arraycopy(bytes, where, bytes, tailOffset, tailLength);
137 Arrays.fill(bytes, where, tailOffset, (byte)0);