Migrate ietf-type-util to JDT annotations
[mdsal.git] / model / ietf / ietf-type-util / src / main / java / org / opendaylight / mdsal / model / ietf / util / Ipv6Utils.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.model.ietf.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12
13 import java.util.Arrays;
14 import org.eclipse.jdt.annotation.NonNull;
15
16 /**
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
20  * the YANG model.
21  */
22 /*
23  * v6 routines added by Anton Ivanov on 14.6.2015
24  * revised by Robert Varga
25  *
26  * BIG FAT WARNING!!!
27  * Read all of the following before you touch any v6 code or decide to
28  * optimize it by invoking a "simple" Guava call
29  *
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
35  *      underneath.
36  *
37  * This is why we have to parse v6 by ourselves.
38  *
39  * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
40  *
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.
45  */
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;
50
51     private Ipv6Utils() {
52         throw new UnsupportedOperationException();
53     }
54
55     /**
56      * Convert Ipv6Address object to a valid Canonical v6 address in byte format
57      *
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
62      */
63     static void fillIpv6Bytes(final byte @NonNull[] bytes, final String str, final int strLimit) {
64        // Leading :: requires some special handling.
65        int i = 0;
66        if (str.charAt(i) == ':') {
67            // Note ++i side-effect in check
68            checkArgument(str.charAt(++i) == ':', "Invalid v6 address '%s'", str);
69        }
70
71        boolean haveVal = false;
72        int val = 0;
73        int colonp = -1;
74        int j = 0;
75        int curtok = i;
76        while (i < strLimit) {
77            final char ch = str.charAt(i++);
78
79            // v6 separator
80            if (ch == ':') {
81                curtok = i;
82                if (haveVal) {
83                    // removed overrun check - the regexp checks for valid data
84                    bytes[j++] = (byte) (val >>> 8 & 0xff);
85                    bytes[j++] = (byte) (val & 0xff);
86                    haveVal = false;
87                    val = 0;
88                } else {
89                    // no need to check separator position validity - regexp does that
90                    colonp = j;
91                }
92
93                continue;
94            }
95
96            // frankenstein - v4 attached to v6, mixed notation
97            if (ch == '.' && j + INADDR4SZ <= INADDR6SZ) {
98                /*
99                 * This has passed the regexp so it is fairly safe to parse it
100                 * straight away. Use the Ipv4Utils for that.
101                 */
102                Ipv4Utils.fillIpv4Bytes(bytes, j, str, curtok, strLimit);
103                j += INADDR4SZ;
104                haveVal = false;
105                break;
106            }
107
108            /*
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.
113             */
114            final int chval = AbstractIetfYangUtil.hexValue(ch);
115            val = val << 4 | chval;
116            haveVal = true;
117        }
118
119        if (haveVal) {
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);
123        }
124
125        if (colonp != -1) {
126            verify(j != INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
127            expandZeros(bytes, colonp, j);
128        } else {
129            verify(j == INADDR6SZ, "Overrun in parsing of '%s', should not occur", str);
130        }
131    }
132
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);
138     }
139 }