1 /*
   2  * Copyright (c) 2019, 2020, 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 package sun.nio.ch;
  27 
  28 import java.net.Inet4Address;
  29 import java.net.Inet6Address;
  30 import java.net.InetAddress;
  31 import java.net.InetSocketAddress;
  32 import java.net.ProtocolFamily;
  33 import java.net.SocketException;
  34 import java.net.StandardProtocolFamily;
  35 import java.net.UnknownHostException;
  36 import java.nio.channels.UnsupportedAddressTypeException;
  37 import jdk.incubator.foreign.MemoryAddress;
  38 import jdk.incubator.foreign.MemorySegment;
  39 import jdk.internal.access.JavaNetInetAddressAccess;
  40 import jdk.internal.access.SharedSecrets;
  41 import static sun.nio.ch.NativeSocketLayout.AF_INET;
  42 import static sun.nio.ch.NativeSocketLayout.AF_INET6;
  43 import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN_SIZE;
  44 import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN6_SIZE;
  45 
  46 /**
  47  * A native socket address that is the union of struct sockaddr, struct sockaddr_in,
  48  * and struct sockaddr_in6.
  49  *
  50  * This class is not thread safe.
  51  */
  52 class NativeSocketAddress {
  53     private static final JavaNetInetAddressAccess JNINA = SharedSecrets.getJavaNetInetAddressAccess();
  54 
  55     private final MemorySegment segment;
  56     private final MemoryAddress memoryAddress;
  57     private final long rawAddress;
  58 
  59     long address() {
  60         return rawAddress;
  61     }
  62 
  63     NativeSocketAddress() {
  64         segment = NativeSocketLayout.allocate();
  65         memoryAddress = segment.baseAddress();
  66         rawAddress = memoryAddress.toRawLongValue();
  67     }
  68 
  69     /**
  70      * Allocates an array of native socket addresses.
  71      */
  72     static NativeSocketAddress[] allocate(int count) {
  73         NativeSocketAddress[] array = new NativeSocketAddress[count];
  74         for (int i = 0; i < count; i++) {
  75             try {
  76                 array[i] = new NativeSocketAddress();
  77             } catch (OutOfMemoryError e) {
  78                 freeAll(array);
  79                 throw e;
  80             }
  81         }
  82         return array;
  83     }
  84 
  85     /**
  86      * Frees all non-null native socket addresses in the given array.
  87      */
  88     static void freeAll(NativeSocketAddress[] array) {
  89         for (int i = 0; i < array.length; i++) {
  90             NativeSocketAddress sa = array[i];
  91             if (sa != null) {
  92                 sa.segment.close();
  93             }
  94         }
  95     }
  96 
  97     /**
  98      * Encodes the given InetSocketAddress into this socket address.
  99      * @param protocolFamily protocol family
 100      * @param isa the InetSocketAddress to encode
 101      * @return the size of the socket address (sizeof sockaddr or sockaddr6)
 102      * @throws UnsupportedAddressTypeException if the address type is not supported
 103      */
 104     int encode(ProtocolFamily protocolFamily, InetSocketAddress isa) {
 105         if (protocolFamily == StandardProtocolFamily.INET) {
 106             // struct sockaddr_in
 107             InetAddress ia = isa.getAddress();
 108             if (!(ia instanceof Inet4Address))
 109                 throw new UnsupportedAddressTypeException();
 110             setFamily(AF_INET);
 111             setAddress(AF_INET, ia);
 112             setPort(AF_INET, isa.getPort());
 113             return SOCKADDR_IN_SIZE;
 114         } else {
 115             // struct sockaddr_in6
 116             setFamily(AF_INET6);
 117             setAddress(AF_INET6, isa.getAddress());
 118             setPort(AF_INET6, isa.getPort());
 119             setFlowInfo(0);
 120             return SOCKADDR_IN6_SIZE;
 121         }
 122     }
 123 
 124     /**
 125      * Returns an InetSocketAddress to represent the socket address in this segment.
 126      * @throws SocketException if the socket address is not AF_INET or AF_INET6
 127      */
 128     InetSocketAddress decode() throws SocketException {
 129         int family = getFamily();
 130         if (family != AF_INET && family != AF_INET6)
 131             throw new SocketException("Socket family not recognized");
 132         return new InetSocketAddress(getAddress(family), getPort(family));
 133     }
 134 
 135     @Override
 136     public boolean equals(Object other) {
 137         if (!(other instanceof NativeSocketAddress)) {
 138             return false;
 139         }
 140         return NativeSocketLayout.mismatch(this.memoryAddress, ((NativeSocketAddress)other).memoryAddress) < 0;
 141     }
 142 
 143     @Override
 144     public int hashCode() {
 145         return NativeSocketLayout.hash(memoryAddress, (x,y) -> 31 * x + y);
 146     }
 147 
 148     @Override
 149     public String toString() {
 150         int family = getFamily();
 151         if (family == AF_INET || family == AF_INET6) {
 152             return ((family == AF_INET) ? "AF_INET" : "AF_INET6")
 153                     + ", address=" + getAddress(family) + ", port=" + getPort(family);
 154         } else {
 155             return "<unknown>";
 156         }
 157     }
 158 
 159     /**
 160      * Returns the value of the sa_family field.
 161      */
 162     private int getFamily() {
 163         return NativeSocketLayout.family(memoryAddress);
 164     }
 165 
 166     /**
 167      * Stores the given family in the sa_family field.
 168      */
 169     private void setFamily(int family) {
 170         NativeSocketLayout.setFamily(memoryAddress, family);
 171     }
 172 
 173     /**
 174      * Returns the value of the sin_port or sin6_port field.
 175      */
 176     private int getPort(int family) {
 177         if (family == AF_INET)
 178             return NativeSocketLayout.getIPv4Port(memoryAddress);
 179         else
 180             return NativeSocketLayout.getIPv6Port(memoryAddress);
 181     }
 182 
 183     /**
 184      * Stores the given port number in the sin_port or sin6_port field.
 185      */
 186     private void setPort(int family, int port) {
 187         if (family == AF_INET)
 188            NativeSocketLayout.setIPv4Port(memoryAddress, port);
 189         else
 190             NativeSocketLayout.setIPv6Port(memoryAddress, port);
 191     }
 192 
 193     /**
 194      * Returns an InetAddress to represent the value of the address in the
 195      * sin4_addr or sin6_addr fields. For IPv6 addresses, the Inet6Address is
 196      * created with the sin6_scope_id in the sockaddr_in6 structure.
 197      */
 198     private InetAddress getAddress(int family) {
 199         final MemoryAddress address = memoryAddress;
 200         int scope_id;
 201         byte[] bytes;
 202 
 203         if (family == AF_INET) {
 204             bytes = NativeSocketLayout.getIPv4Address(address);
 205             scope_id = 0;
 206         } else {
 207             bytes = NativeSocketLayout.getIPv6Address(address);
 208             scope_id = NativeSocketLayout.getScopeId(address);
 209         }
 210         try {
 211             if (scope_id == 0) {
 212                 return InetAddress.getByAddress(bytes);
 213             } else {
 214                 return Inet6Address.getByAddress(null, bytes, scope_id);
 215             }
 216         } catch (UnknownHostException e) {
 217             throw new InternalError(e);
 218         }
 219     }
 220 
 221     /**
 222      * Stores the given InetAddress in the sin_addr or sin6_addr/sin6_scope_id
 223      * fields. For IPv6 addresses, the sin6_addr will be popluated with an
 224      * IPv4-mapped IPv6 address when the given InetAddress is an IPv4 address.
 225      */
 226     private void setAddress(int family, InetAddress ia) {
 227         final MemoryAddress address = memoryAddress;
 228         if (family == AF_INET) {
 229             // IPv4 address
 230             NativeSocketLayout.setIPv4Address(address, JNINA.addressValue((Inet4Address) ia));
 231         } else {
 232             int scope_id;
 233             if (ia instanceof Inet4Address) {
 234                 // IPv4-mapped IPv6 address
 235                 NativeSocketLayout.setMappedAddress(address, JNINA.addressValue((Inet4Address) ia));
 236                 scope_id = 0;
 237             } else {
 238                 // IPv6 address
 239                 var inet6Address = (Inet6Address) ia;
 240                 NativeSocketLayout.setIPv6Address(address, JNINA.addressBytes(inet6Address));
 241                 scope_id = inet6Address.getScopeId();
 242             }
 243             NativeSocketLayout.setScopeId(address, scope_id);
 244         }
 245     }
 246 
 247     private void setFlowInfo(int flowInfo) {
 248         NativeSocketLayout.setFlowInfo(memoryAddress, flowInfo);
 249     }
 250 }