72c8bf9c12dd603795b62fa79463d267704f239f
[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 com.google.common.base.Preconditions;
11 import com.google.common.base.Verify;
12 import javax.annotation.Nonnull;
13
14 /**
15  * IPv6 address parsing for ietf-inet-types ipv6-address and ipv6-prefix. This is an internal implementation
16  * class, not meant to be exposed in any shape or form to the outside world, as the code relies on the fact that
17  * the strings presented to it have been previously validated to conform to the regular expressions defined in
18  * the YANG model.
19  */
20 /*
21  * v6 routines added by Anton Ivanov on 14.6.2015
22  * revised by Robert Varga
23  *
24  * BIG FAT WARNING!!!
25  * Read all of the following before you touch any v6 code or decide to
26  * optimize it by invoking a "simple" Guava call
27  *
28  * Java IPv6 is fundamentally broken and Google libraries do not fix it.
29  * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
30  *      and there is absolutely no way to override this behaviour
31  * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
32  *      exception. Even if they did, they re-use the same broken java code
33  *      underneath.
34  *
35  * This is why we have to parse v6 by ourselves.
36  *
37  * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
38  *
39  * The original BSD code is licensed under standard BSD license. While we
40  * are not obliged to provide an attribution, credit where credit is due.
41  * As far as why it is similar to Sun's sun.net.util please ask Sun why
42  * their code has the same variable names, comments and code flow.
43  */
44 final class Ipv6Utils {
45     private static final int INADDR4SZ = 4;
46     private static final int INADDR6SZ = 16;
47     private static final int INT16SZ = Short.BYTES;
48
49     private Ipv6Utils() {
50         throw new UnsupportedOperationException();
51     }
52
53     /**
54      * Convert Ipv6Address object to a valid Canonical v6 address in byte format
55      *
56      * @param addrStr IPv6 address string
57      * @return byte array of size 16 containing the binary IPv6 address
58      * @throws NullPointerException if ipv6address is null
59      */
60    public static @Nonnull byte[] bytesForString(final @Nonnull String addrStr) {
61        final int percentPos = addrStr.indexOf('%');
62        final int addrStrLen = percentPos == -1 ? addrStr.length() : percentPos;
63
64        // Leading :: requires some special handling.
65        int i = 0;
66        if (addrStr.charAt(i) == ':') {
67            // Note ++i side-effect in check
68            Preconditions.checkArgument(addrStr.charAt(++i) == ':', "Invalid v6 address '%s'", addrStr);
69        }
70
71        final byte[] dst = new byte[INADDR6SZ];
72
73        boolean haveVal = false;
74        int val = 0;
75        int colonp = -1;
76        int j = 0;
77        int curtok = i;
78        while (i < addrStrLen) {
79            final char ch = addrStr.charAt(i++);
80
81            // v6 separator
82            if (ch == ':') {
83                curtok = i;
84                if (haveVal) {
85                    // removed overrun check - the regexp checks for valid data
86                    dst[j++] = (byte) ((val >>> 8) & 0xff);
87                    dst[j++] = (byte) (val & 0xff);
88                    haveVal = false;
89                    val = 0;
90                } else {
91                    // no need to check separator position validity - regexp does that
92                    colonp = j;
93                }
94
95                continue;
96            }
97
98            // frankenstein - v4 attached to v6, mixed notation
99            if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
100                /*
101                 * This has passed the regexp so it is fairly safe to parse it
102                 * straight away. Use the Ipv4Utils for that.
103                 */
104                Ipv4Utils.fillIpv4Bytes(dst, j, addrStr, curtok, addrStrLen);
105                j += INADDR4SZ;
106                haveVal = false;
107                break;
108            }
109
110            /*
111             * Business as usual - ipv6 address digit.
112             * We can remove all checks from the original BSD code because
113             * the regexp has already verified that we are not being fed
114             * anything bigger than 0xffff between the separators.
115             */
116            final int chval = AbstractIetfYangUtil.hexValue(ch);
117            val = (val << 4) | chval;
118            haveVal = true;
119        }
120
121        if (haveVal) {
122            Verify.verify(j + INT16SZ <= INADDR6SZ, "Overrun in parsing of '%s', should not occur", addrStr);
123            dst[j++] = (byte) ((val >> 8) & 0xff);
124            dst[j++] = (byte) (val & 0xff);
125        }
126
127        if (colonp != -1) {
128            Verify.verify(j != INADDR6SZ, "Overrun in parsing of '%s', should not occur", addrStr);
129
130            final int n = j - colonp;
131            for (i = 1; i <= n; i++) {
132                dst[INADDR6SZ - i] = dst[j - i];
133                dst[j - i] = 0;
134            }
135        } else {
136            Verify.verify(j == INADDR6SZ, "Overrun in parsing of '%s', should not occur", addrStr);
137        }
138
139        return dst;
140    }
141 }