1/*
2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *
28 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
30 */
31
32package sun.security.krb5.internal;
33
34import sun.security.krb5.Config;
35import sun.security.krb5.PrincipalName;
36import sun.security.krb5.KrbException;
37import sun.security.krb5.Asn1Exception;
38import sun.security.util.*;
39
40import java.net.*;
41import java.util.*;
42import java.io.IOException;
43import sun.security.krb5.internal.ccache.CCacheOutputStream;
44
45/**
46 * Implements the ASN.1 HostAddresses type.
47 *
48 * <pre>{@code
49 * HostAddresses   -- NOTE: subtly different from rfc1510,
50 *                 -- but has a value mapping and encodes the same
51 *         ::= SEQUENCE OF HostAddress
52 *
53 * HostAddress     ::= SEQUENCE  {
54 *         addr-type       [0] Int32,
55 *         address         [1] OCTET STRING
56 * }
57 * }</pre>
58 *
59 * <p>
60 * This definition reflects the Network Working Group RFC 4120
61 * specification available at
62 * <a href="http://www.ietf.org/rfc/rfc4120.txt">
63 * http://www.ietf.org/rfc/rfc4120.txt</a>.
64 */
65
66public class HostAddresses implements Cloneable {
67    private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
68    private HostAddress[] addresses = null;
69    private volatile int hashCode = 0;
70
71    public HostAddresses(HostAddress[] new_addresses) throws IOException {
72        if (new_addresses != null) {
73           addresses = new HostAddress[new_addresses.length];
74           for (int i = 0; i < new_addresses.length; i++) {
75                if (new_addresses[i] == null) {
76                   throw new IOException("Cannot create a HostAddress");
77                } else {
78                   addresses[i] = (HostAddress)new_addresses[i].clone();
79                }
80           }
81        }
82    }
83
84    public HostAddresses() throws UnknownHostException {
85        addresses = new HostAddress[1];
86        addresses[0] = new HostAddress();
87    }
88
89    private HostAddresses(int dummy) {}
90
91    public HostAddresses(PrincipalName serverPrincipal)
92        throws UnknownHostException, KrbException {
93
94        String[] components = serverPrincipal.getNameStrings();
95
96        if (serverPrincipal.getNameType() != PrincipalName.KRB_NT_SRV_HST ||
97            components.length < 2)
98            throw new KrbException(Krb5.KRB_ERR_GENERIC, "Bad name");
99
100        String host = components[1];
101        InetAddress[] addr = InetAddress.getAllByName(host);
102        HostAddress[] hAddrs = new HostAddress[addr.length];
103
104        for (int i = 0; i < addr.length; i++) {
105            hAddrs[i] = new HostAddress(addr[i]);
106        }
107
108        addresses = hAddrs;
109    }
110
111    public Object clone() {
112        HostAddresses new_hostAddresses = new HostAddresses(0);
113        if (addresses != null) {
114            new_hostAddresses.addresses = new HostAddress[addresses.length];
115            for (int i = 0; i < addresses.length; i++) {
116                new_hostAddresses.addresses[i] =
117                        (HostAddress)addresses[i].clone();
118            }
119        }
120        return new_hostAddresses;
121    }
122
123    public boolean inList(HostAddress addr) {
124        if (addresses != null) {
125            for (int i = 0; i < addresses.length; i++)
126                if (addresses[i].equals(addr))
127                    return true;
128        }
129        return false;
130    }
131
132    public int hashCode() {
133        if (hashCode == 0) {
134            int result = 17;
135            if (addresses != null) {
136                for (int i=0; i < addresses.length; i++)  {
137                    result = 37*result + addresses[i].hashCode();
138                }
139            }
140            hashCode = result;
141        }
142        return hashCode;
143
144    }
145
146
147    public boolean equals(Object obj) {
148        if (this == obj) {
149            return true;
150        }
151
152        if (!(obj instanceof HostAddresses)) {
153            return false;
154        }
155
156        HostAddresses addrs = (HostAddresses)obj;
157        if ((addresses == null && addrs.addresses != null) ||
158            (addresses != null && addrs.addresses == null))
159            return false;
160        if (addresses != null && addrs.addresses != null) {
161            if (addresses.length != addrs.addresses.length)
162                return false;
163            for (int i = 0; i < addresses.length; i++)
164                if (!addresses[i].equals(addrs.addresses[i]))
165                    return false;
166        }
167        return true;
168    }
169
170   /**
171    * Constructs a new <code>HostAddresses</code> object.
172    * @param encoding a single DER-encoded value.
173    * @exception Asn1Exception if an error occurs while decoding an
174    * ASN1 encoded data.
175    * @exception IOException if an I/O error occurs while reading
176    * encoded data.
177    */
178    public HostAddresses(DerValue encoding)
179        throws  Asn1Exception, IOException {
180        Vector<HostAddress> tempAddresses = new Vector<>();
181        DerValue der = null;
182        while (encoding.getData().available() > 0) {
183            der = encoding.getData().getDerValue();
184            tempAddresses.addElement(new HostAddress(der));
185        }
186        if (tempAddresses.size() > 0) {
187            addresses = new HostAddress[tempAddresses.size()];
188            tempAddresses.copyInto(addresses);
189        }
190    }
191
192
193   /**
194    * Encodes a <code>HostAddresses</code> object.
195    * @return byte array of encoded <code>HostAddresses</code> object.
196    * @exception Asn1Exception if an error occurs while decoding an
197    * ASN1 encoded data.
198    * @exception IOException if an I/O error occurs while reading
199    * encoded data.
200    */
201    public byte[] asn1Encode() throws Asn1Exception, IOException {
202        DerOutputStream bytes = new DerOutputStream();
203        DerOutputStream temp = new DerOutputStream();
204
205        if (addresses != null && addresses.length > 0) {
206            for (int i = 0; i < addresses.length; i++)
207                bytes.write(addresses[i].asn1Encode());
208        }
209        temp.write(DerValue.tag_Sequence, bytes);
210        return temp.toByteArray();
211    }
212
213    /**
214     * Parse (unmarshal) a <code>HostAddresses</code> from a DER input stream.
215     * This form
216     * parsing might be used when expanding a value which is part of
217     * a constructed sequence and uses explicitly tagged type.
218     *
219     * @exception Asn1Exception if an Asn1Exception occurs.
220     * @param data the Der input stream value, which contains one or more
221     * marshaled value.
222     * @param explicitTag tag number.
223     * @param optional indicates if this data field is optional.
224     * @return an instance of <code>HostAddresses</code>.
225     */
226    public static HostAddresses parse(DerInputStream data,
227                                      byte explicitTag, boolean optional)
228        throws Asn1Exception, IOException {
229        if ((optional) &&
230            (((byte)data.peekByte() & (byte)0x1F) != explicitTag))
231            return null;
232        DerValue der = data.getDerValue();
233        if (explicitTag != (der.getTag() & (byte)0x1F))  {
234            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
235        } else {
236            DerValue subDer = der.getData().getDerValue();
237            return new HostAddresses(subDer);
238        }
239    }
240
241    /**
242         * Writes data field values in <code>HostAddresses</code> in FCC
243         * format to a <code>CCacheOutputStream</code>.
244         *
245         * @param cos a <code>CCacheOutputStream</code> to be written to.
246         * @exception IOException if an I/O exception occurs.
247         * @see sun.security.krb5.internal.ccache.CCacheOutputStream
248         */
249
250    public void writeAddrs(CCacheOutputStream cos) throws IOException {
251        if (addresses == null || addresses.length == 0) {
252            cos.write32(0);
253            return;
254        }
255        cos.write32(addresses.length);
256        for (int i = 0; i < addresses.length; i++) {
257            cos.write16(addresses[i].addrType);
258            cos.write32(addresses[i].address.length);
259            cos.write(addresses[i].address, 0,
260                      addresses[i].address.length);
261        }
262    }
263
264
265    public InetAddress[] getInetAddresses() {
266
267        if (addresses == null || addresses.length == 0)
268            return null;
269
270        ArrayList<InetAddress> ipAddrs = new ArrayList<>(addresses.length);
271
272        for (int i = 0; i < addresses.length; i++) {
273            try {
274                if ((addresses[i].addrType == Krb5.ADDRTYPE_INET) ||
275                    (addresses[i].addrType == Krb5.ADDRTYPE_INET6)) {
276                    ipAddrs.add(addresses[i].getInetAddress());
277                }
278            } catch (java.net.UnknownHostException e) {
279                // Should not happen since IP address given
280                return null;
281            }
282        }
283
284        InetAddress[] retVal = new InetAddress[ipAddrs.size()];
285        return ipAddrs.toArray(retVal);
286
287    }
288
289    /**
290     * Returns all the IP addresses of the local host.
291     */
292    public static HostAddresses getLocalAddresses() throws IOException
293    {
294        Set<InetAddress> all = new LinkedHashSet<>();
295        try {
296            if (DEBUG) {
297                System.out.println(">>> KrbKdcReq local addresses are:");
298            }
299            String extra = Config.getInstance().getAll(
300                    "libdefaults", "extra_addresses");
301            if (extra != null) {
302                for (String s: extra.split("\\s+")) {
303                    all.add(InetAddress.getByName(s));
304                    if (DEBUG) {
305                        System.out.println("   extra_addresses: "
306                                + InetAddress.getByName(s));
307                    }
308                }
309            }
310            for (NetworkInterface ni:
311                    Collections.list(NetworkInterface.getNetworkInterfaces())) {
312                if (DEBUG) {
313                    System.out.println("   NetworkInterface " + ni + ":");
314                    System.out.println("      "
315                            + Collections.list(ni.getInetAddresses()));
316                }
317                all.addAll(Collections.list(ni.getInetAddresses()));
318            }
319            return new HostAddresses(all.toArray(new InetAddress[all.size()]));
320        } catch (Exception exc) {
321            throw new IOException(exc.toString());
322        }
323    }
324
325    /**
326     * Creates a new HostAddresses instance from the supplied list
327     * of InetAddresses.
328     */
329    public HostAddresses(InetAddress[] inetAddresses)
330    {
331        if (inetAddresses == null)
332            {
333                addresses = null;
334                return;
335            }
336
337        addresses = new HostAddress[inetAddresses.length];
338        for (int i = 0; i < inetAddresses.length; i++)
339            addresses[i] = new HostAddress(inetAddresses[i]);
340    }
341
342    @Override
343    public String toString() {
344        return Arrays.toString(addresses);
345    }
346}
347