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 }