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 
  32 package sun.security.krb5.internal;
  33 
  34 import sun.security.krb5.PrincipalName;
  35 import sun.security.krb5.KrbException;
  36 import sun.security.krb5.Asn1Exception;
  37 import sun.security.util.*;
  38 import java.util.Vector;
  39 import java.util.ArrayList;
  40 import java.net.InetAddress;
  41 import java.net.Inet4Address;
  42 import java.net.Inet6Address;
  43 import java.net.UnknownHostException;
  44 import java.io.IOException;
  45 import sun.security.krb5.internal.ccache.CCacheOutputStream;
  46 
  47 /**
  48  * Implements the ASN.1 HostAddresses type.
  49  *
  50  * <xmp>
  51  * HostAddresses   -- NOTE: subtly different from rfc1510,
  52  *                 -- but has a value mapping and encodes the same
  53  *         ::= SEQUENCE OF HostAddress
  54  *
  55  * HostAddress     ::= SEQUENCE  {
  56  *         addr-type       [0] Int32,
  57  *         address         [1] OCTET STRING
  58  * }
  59  * </xmp>
  60  *
  61  * <p>
  62  * This definition reflects the Network Working Group RFC 4120
  63  * specification available at
  64  * <a href="http://www.ietf.org/rfc/rfc4120.txt">
  65  * http://www.ietf.org/rfc/rfc4120.txt</a>.
  66  */
  67 
  68 public class HostAddresses implements Cloneable {
  69     private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
  70     private HostAddress[] addresses = null;
  71     private volatile int hashCode = 0;
  72 
  73     public HostAddresses(HostAddress[] new_addresses) throws IOException {
  74         if (new_addresses != null) {
  75            addresses = new HostAddress[new_addresses.length];
  76            for (int i = 0; i < new_addresses.length; i++) {
  77                 if (new_addresses[i] == null) {
  78                    throw new IOException("Cannot create a HostAddress");
  79                 } else {
  80                    addresses[i] = (HostAddress)new_addresses[i].clone();
  81                 }
  82            }
  83         }
  84     }
  85 
  86     public HostAddresses() throws UnknownHostException {
  87         addresses = new HostAddress[1];
  88         addresses[0] = new HostAddress();
  89     }
  90 
  91     private HostAddresses(int dummy) {}
  92 
  93     public HostAddresses(PrincipalName serverPrincipal)
  94         throws UnknownHostException, KrbException {
  95 
  96         String[] components = serverPrincipal.getNameStrings();
  97 
  98         if (serverPrincipal.getNameType() != PrincipalName.KRB_NT_SRV_HST ||
  99             components.length < 2)
 100             throw new KrbException(Krb5.KRB_ERR_GENERIC, "Bad name");
 101 
 102         String host = components[1];
 103         InetAddress addr[] = InetAddress.getAllByName(host);
 104         HostAddress hAddrs[] = new HostAddress[addr.length];
 105 
 106         for (int i = 0; i < addr.length; i++) {
 107             hAddrs[i] = new HostAddress(addr[i]);
 108         }
 109 
 110         addresses = hAddrs;
 111     }
 112 
 113     public Object clone() {
 114         HostAddresses new_hostAddresses = new HostAddresses(0);
 115         if (addresses != null) {
 116             new_hostAddresses.addresses = new HostAddress[addresses.length];
 117             for (int i = 0; i < addresses.length; i++) {
 118                 new_hostAddresses.addresses[i] =
 119                         (HostAddress)addresses[i].clone();
 120             }
 121         }
 122         return new_hostAddresses;
 123     }
 124 
 125     public boolean inList(HostAddress addr) {
 126         if (addresses != null) {
 127             for (int i = 0; i < addresses.length; i++)
 128                 if (addresses[i].equals(addr))
 129                     return true;
 130         }
 131         return false;
 132     }
 133 
 134     public int hashCode() {
 135         if (hashCode == 0) {
 136             int result = 17;
 137             if (addresses != null) {
 138                 for (int i=0; i < addresses.length; i++)  {
 139                     result = 37*result + addresses[i].hashCode();
 140                 }
 141             }
 142             hashCode = result;
 143         }
 144         return hashCode;
 145 
 146     }
 147 
 148 
 149     public boolean equals(Object obj) {
 150         if (this == obj) {
 151             return true;
 152         }
 153 
 154         if (!(obj instanceof HostAddresses)) {
 155             return false;
 156         }
 157 
 158         HostAddresses addrs = (HostAddresses)obj;
 159         if ((addresses == null && addrs.addresses != null) ||
 160             (addresses != null && addrs.addresses == null))
 161             return false;
 162         if (addresses != null && addrs.addresses != null) {
 163             if (addresses.length != addrs.addresses.length)
 164                 return false;
 165             for (int i = 0; i < addresses.length; i++)
 166                 if (!addresses[i].equals(addrs.addresses[i]))
 167                     return false;
 168         }
 169         return true;
 170     }
 171 
 172    /**
 173     * Constructs a new <code>HostAddresses</code> object.
 174     * @param encoding a single DER-encoded value.
 175     * @exception Asn1Exception if an error occurs while decoding an
 176     * ASN1 encoded data.
 177     * @exception IOException if an I/O error occurs while reading
 178     * encoded data.
 179     */
 180     public HostAddresses(DerValue encoding)
 181         throws  Asn1Exception, IOException {
 182         Vector<HostAddress> tempAddresses = new Vector<>();
 183         DerValue der = null;
 184         while (encoding.getData().available() > 0) {
 185             der = encoding.getData().getDerValue();
 186             tempAddresses.addElement(new HostAddress(der));
 187         }
 188         if (tempAddresses.size() > 0) {
 189             addresses = new HostAddress[tempAddresses.size()];
 190             tempAddresses.copyInto(addresses);
 191         }
 192     }
 193 
 194 
 195    /**
 196     * Encodes a <code>HostAddresses</code> object.
 197     * @return byte array of encoded <code>HostAddresses</code> object.
 198     * @exception Asn1Exception if an error occurs while decoding an
 199     * ASN1 encoded data.
 200     * @exception IOException if an I/O error occurs while reading
 201     * encoded data.
 202     */
 203     public byte[] asn1Encode() throws Asn1Exception, IOException {
 204         DerOutputStream bytes = new DerOutputStream();
 205         DerOutputStream temp = new DerOutputStream();
 206 
 207         if (addresses != null && addresses.length > 0) {
 208             for (int i = 0; i < addresses.length; i++)
 209                 bytes.write(addresses[i].asn1Encode());
 210         }
 211         temp.write(DerValue.tag_Sequence, bytes);
 212         return temp.toByteArray();
 213     }
 214 
 215     /**
 216      * Parse (unmarshal) a <code>HostAddresses</code> from a DER input stream.
 217      * This form
 218      * parsing might be used when expanding a value which is part of
 219      * a constructed sequence and uses explicitly tagged type.
 220      *
 221      * @exception Asn1Exception if an Asn1Exception occurs.
 222      * @param data the Der input stream value, which contains one or more
 223      * marshaled value.
 224      * @param explicitTag tag number.
 225      * @param optional indicates if this data field is optional.
 226      * @return an instance of <code>HostAddresses</code>.
 227      */
 228     public static HostAddresses parse(DerInputStream data,
 229                                       byte explicitTag, boolean optional)
 230         throws Asn1Exception, IOException {
 231         if ((optional) &&
 232             (((byte)data.peekByte() & (byte)0x1F) != explicitTag))
 233             return null;
 234         DerValue der = data.getDerValue();
 235         if (explicitTag != (der.getTag() & (byte)0x1F))  {
 236             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 237         } else {
 238             DerValue subDer = der.getData().getDerValue();
 239             return new HostAddresses(subDer);
 240         }
 241     }
 242 
 243     /**
 244          * Writes data field values in <code>HostAddresses</code> in FCC
 245          * format to a <code>CCacheOutputStream</code>.
 246          *
 247          * @param cos a <code>CCacheOutputStream</code> to be written to.
 248          * @exception IOException if an I/O exception occurs.
 249          * @see sun.security.krb5.internal.ccache.CCacheOutputStream
 250          */
 251 
 252     public void writeAddrs(CCacheOutputStream cos) throws IOException {
 253         cos.write32(addresses.length);
 254         for (int i = 0; i < addresses.length; i++) {
 255             cos.write16(addresses[i].addrType);
 256             cos.write32(addresses[i].address.length);
 257             cos.write(addresses[i].address, 0,
 258                       addresses[i].address.length);
 259         }
 260     }
 261 
 262 
 263     public InetAddress[] getInetAddresses() {
 264 
 265         if (addresses == null || addresses.length == 0)
 266             return null;
 267 
 268         ArrayList<InetAddress> ipAddrs = new ArrayList<>(addresses.length);
 269 
 270         for (int i = 0; i < addresses.length; i++) {
 271             try {
 272                 if ((addresses[i].addrType == Krb5.ADDRTYPE_INET) ||
 273                     (addresses[i].addrType == Krb5.ADDRTYPE_INET6)) {
 274                     ipAddrs.add(addresses[i].getInetAddress());
 275                 }
 276             } catch (java.net.UnknownHostException e) {
 277                 // Should not happen since IP address given
 278                 return null;
 279             }
 280         }
 281 
 282         InetAddress[] retVal = new InetAddress[ipAddrs.size()];
 283         return ipAddrs.toArray(retVal);
 284 
 285     }
 286 
 287     /**
 288      * Returns all the IP addresses of the local host.
 289      */
 290     public static HostAddresses getLocalAddresses() throws IOException
 291     {
 292         String hostname = null;
 293         InetAddress[] inetAddresses = null;
 294         try {
 295             InetAddress localHost = InetAddress.getLocalHost();
 296             hostname = localHost.getHostName();
 297             inetAddresses = InetAddress.getAllByName(hostname);
 298             HostAddress[] hAddresses = new HostAddress[inetAddresses.length];
 299             for (int i = 0; i < inetAddresses.length; i++)
 300                 {
 301                     hAddresses[i] = new HostAddress(inetAddresses[i]);
 302                 }
 303             if (DEBUG) {
 304                 System.out.println(">>> KrbKdcReq local addresses for "
 305                                    + hostname + " are: ");
 306 
 307                 for (int i = 0; i < inetAddresses.length; i++) {
 308                     System.out.println("\n\t" + inetAddresses[i]);
 309                     if (inetAddresses[i] instanceof Inet4Address)
 310                         System.out.println("IPv4 address");
 311                     if (inetAddresses[i] instanceof Inet6Address)
 312                         System.out.println("IPv6 address");
 313                 }
 314             }
 315             return (new HostAddresses(hAddresses));
 316         } catch (Exception exc) {
 317             throw new IOException(exc.toString());
 318         }
 319 
 320     }
 321 
 322     /**
 323      * Creates a new HostAddresses instance from the supplied list
 324      * of InetAddresses.
 325      */
 326     public HostAddresses(InetAddress[] inetAddresses)
 327     {
 328         if (inetAddresses == null)
 329             {
 330                 addresses = null;
 331                 return;
 332             }
 333 
 334         addresses = new HostAddress[inetAddresses.length];
 335         for (int i = 0; i < inetAddresses.length; i++)
 336             addresses[i] = new HostAddress(inetAddresses[i]);
 337     }
 338 }