/* * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.ch; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ProtocolFamily; import java.net.SocketException; import java.net.StandardProtocolFamily; import java.net.UnknownHostException; import java.nio.channels.UnsupportedAddressTypeException; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.internal.access.JavaNetInetAddressAccess; import jdk.internal.access.SharedSecrets; import static sun.nio.ch.NativeSocketLayout.AF_INET; import static sun.nio.ch.NativeSocketLayout.AF_INET6; import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN_SIZE; import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN6_SIZE; /** * A native socket address that is the union of struct sockaddr, struct sockaddr_in, * and struct sockaddr_in6. * * This class is not thread safe. */ class NativeSocketAddress { private static final JavaNetInetAddressAccess JNINA = SharedSecrets.getJavaNetInetAddressAccess(); private final MemorySegment segment; private final MemoryAddress memoryAddress; private final long rawAddress; long address() { return rawAddress; } NativeSocketAddress() { segment = NativeSocketLayout.allocate(); memoryAddress = segment.baseAddress(); rawAddress = memoryAddress.toRawLongValue(); } /** * Allocates an array of native socket addresses. */ static NativeSocketAddress[] allocate(int count) { NativeSocketAddress[] array = new NativeSocketAddress[count]; for (int i = 0; i < count; i++) { try { array[i] = new NativeSocketAddress(); } catch (OutOfMemoryError e) { freeAll(array); throw e; } } return array; } /** * Frees all non-null native socket addresses in the given array. */ static void freeAll(NativeSocketAddress[] array) { for (int i = 0; i < array.length; i++) { NativeSocketAddress sa = array[i]; if (sa != null) { sa.segment.close(); } } } /** * Encodes the given InetSocketAddress into this socket address. * @param protocolFamily protocol family * @param isa the InetSocketAddress to encode * @return the size of the socket address (sizeof sockaddr or sockaddr6) * @throws UnsupportedAddressTypeException if the address type is not supported */ int encode(ProtocolFamily protocolFamily, InetSocketAddress isa) { if (protocolFamily == StandardProtocolFamily.INET) { // struct sockaddr_in InetAddress ia = isa.getAddress(); if (!(ia instanceof Inet4Address)) throw new UnsupportedAddressTypeException(); setFamily(AF_INET); setAddress(AF_INET, ia); setPort(AF_INET, isa.getPort()); return SOCKADDR_IN_SIZE; } else { // struct sockaddr_in6 setFamily(AF_INET6); setAddress(AF_INET6, isa.getAddress()); setPort(AF_INET6, isa.getPort()); setFlowInfo(0); return SOCKADDR_IN6_SIZE; } } /** * Returns an InetSocketAddress to represent the socket address in this segment. * @throws SocketException if the socket address is not AF_INET or AF_INET6 */ InetSocketAddress decode() throws SocketException { int family = getFamily(); if (family != AF_INET && family != AF_INET6) throw new SocketException("Socket family not recognized"); return new InetSocketAddress(getAddress(family), getPort(family)); } @Override public boolean equals(Object other) { if (!(other instanceof NativeSocketAddress)) { return false; } return NativeSocketLayout.mismatch(this.memoryAddress, ((NativeSocketAddress)other).memoryAddress) < 0; } @Override public int hashCode() { return NativeSocketLayout.hash(memoryAddress, (x,y) -> 31 * x + y); } @Override public String toString() { int family = getFamily(); if (family == AF_INET || family == AF_INET6) { return ((family == AF_INET) ? "AF_INET" : "AF_INET6") + ", address=" + getAddress(family) + ", port=" + getPort(family); } else { return ""; } } /** * Returns the value of the sa_family field. */ private int getFamily() { return NativeSocketLayout.family(memoryAddress); } /** * Stores the given family in the sa_family field. */ private void setFamily(int family) { NativeSocketLayout.setFamily(memoryAddress, family); } /** * Returns the value of the sin_port or sin6_port field. */ private int getPort(int family) { if (family == AF_INET) return NativeSocketLayout.getIPv4Port(memoryAddress); else return NativeSocketLayout.getIPv6Port(memoryAddress); } /** * Stores the given port number in the sin_port or sin6_port field. */ private void setPort(int family, int port) { if (family == AF_INET) NativeSocketLayout.setIPv4Port(memoryAddress, port); else NativeSocketLayout.setIPv6Port(memoryAddress, port); } /** * Returns an InetAddress to represent the value of the address in the * sin4_addr or sin6_addr fields. For IPv6 addresses, the Inet6Address is * created with the sin6_scope_id in the sockaddr_in6 structure. */ private InetAddress getAddress(int family) { final MemoryAddress address = memoryAddress; int scope_id; byte[] bytes; if (family == AF_INET) { bytes = NativeSocketLayout.getIPv4Address(address); scope_id = 0; } else { bytes = NativeSocketLayout.getIPv6Address(address); scope_id = NativeSocketLayout.getScopeId(address); } try { if (scope_id == 0) { return InetAddress.getByAddress(bytes); } else { return Inet6Address.getByAddress(null, bytes, scope_id); } } catch (UnknownHostException e) { throw new InternalError(e); } } /** * Stores the given InetAddress in the sin_addr or sin6_addr/sin6_scope_id * fields. For IPv6 addresses, the sin6_addr will be popluated with an * IPv4-mapped IPv6 address when the given InetAddress is an IPv4 address. */ private void setAddress(int family, InetAddress ia) { final MemoryAddress address = memoryAddress; if (family == AF_INET) { // IPv4 address NativeSocketLayout.setIPv4Address(address, JNINA.addressValue((Inet4Address) ia)); } else { int scope_id; if (ia instanceof Inet4Address) { // IPv4-mapped IPv6 address NativeSocketLayout.setMappedAddress(address, JNINA.addressValue((Inet4Address) ia)); scope_id = 0; } else { // IPv6 address var inet6Address = (Inet6Address) ia; NativeSocketLayout.setIPv6Address(address, JNINA.addressBytes(inet6Address)); scope_id = inet6Address.getScopeId(); } NativeSocketLayout.setScopeId(address, scope_id); } } private void setFlowInfo(int flowInfo) { NativeSocketLayout.setFlowInfo(memoryAddress, flowInfo); } }