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