BUG-2825: rename method name
[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.net.InetAddresses;
12 import java.net.Inet4Address;
13 import java.net.InetAddress;
14
15 /**
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
19  * the YANG model.
20  */
21 /*
22  * v6 routines added by Anton Ivanov on 14.6.2015
23  * revised by Robert Varga
24  *
25  * BIG FAT WARNING!!!
26  * Read all of the following before you touch any v6 code or decide to
27  * optimize it by invoking a "simple" Guava call
28  *
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
34  *      underneath.
35  *
36  * This is why we have to parse v6 by ourselves.
37  *
38  * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
39  *
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.
44  */
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;
49
50     private Ipv6Utils() {
51         throw new UnsupportedOperationException();
52     }
53
54     /**
55      * Convert Ipv6Address object to a valid Canonical v6 address in byte format
56      *
57      * @param ipv6Address - v6 Address object
58      *
59      * FIXME: rovarga: this looks wrong
60      * @return - byte array of size 16. Last byte contains netmask
61      */
62    public static byte[] bytesForString(final String ipv6Address) {
63        /*
64         * Do not modify this routine to take direct strings input!!!
65         * Key checks have been removed based on the assumption that
66         * the input is validated via regexps in Ipv6Prefix()
67         */
68
69        String [] address =  (ipv6Address).split("%");
70
71        int colonp;
72        char ch;
73        boolean saw_xdigit;
74
75        /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
76
77        int val;
78
79        char[] src = address[0].toCharArray();
80
81        byte[] dst = new byte[INADDR6SZ];
82
83        int src_length = src.length;
84
85        colonp = -1;
86        int i = 0, j = 0;
87
88        /* Leading :: requires some special handling. */
89
90        /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
91         * We will derive our code from BSD. Shakespear always sounds better
92         * in original Clingon. So does Dilbert.
93         */
94
95        if (src[i] == ':') {
96            Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
97        }
98
99        int curtok = i;
100        saw_xdigit = false;
101
102
103        val = 0;
104        while (i < src_length) {
105            ch = src[i++];
106            int chval = Character.digit(ch, 16);
107
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.
112             */
113
114            if (chval != -1) {
115                val <<= 4;
116                val |= chval;
117                saw_xdigit = true;
118                continue;
119            }
120
121            /* v6 separator */
122
123            if (ch == ':') {
124                curtok = i;
125                if (!saw_xdigit) {
126                    /* no need to check separator position validity - regexp does that */
127                    colonp = j;
128                    continue;
129                }
130
131                /* removed overrun check - the regexp checks for valid data */
132
133                dst[j++] = (byte) ((val >>> 8) & 0xff);
134                dst[j++] = (byte) (val & 0xff);
135                saw_xdigit = false;
136                val = 0;
137                continue;
138            }
139
140            /* frankenstein - v4 attached to v6, mixed notation */
141
142            if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
143
144                /* this has passed the regexp so it is fairly safe to parse it
145                 * straight away. As v4 addresses do not suffer from the same
146                 * defficiencies as the java v6 implementation we can invoke it
147                 * straight away and be done with it
148                 */
149
150                Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
151
152                InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
153
154                Preconditions.checkArgument(_inet_form instanceof Inet4Address);
155                System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
156                j += INADDR4SZ;
157
158                saw_xdigit = false;
159                break;
160            }
161            /* removed parser exit on invalid char - no need to do it, regexp checks it */
162        }
163        if (saw_xdigit) {
164            Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
165            dst[j++] = (byte) ((val >> 8) & 0xff);
166            dst[j++] = (byte) (val & 0xff);
167        }
168
169        if (colonp != -1) {
170            int n = j - colonp;
171
172            Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
173            for (i = 1; i <= n; i++) {
174                dst[INADDR6SZ - i] = dst[colonp + n - i];
175                dst[colonp + n - i] = 0;
176            }
177            j = INADDR6SZ;
178        }
179
180        Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
181
182        return dst;
183    }
184
185 }