1 /* 2 * Copyright (c) 1996, 2019, 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 package java.net; 26 27 import java.io.FileDescriptor; 28 import java.io.IOException; 29 import java.util.Collections; 30 import java.util.HashSet; 31 import java.util.Objects; 32 import java.util.Set; 33 34 import sun.net.ResourceManager; 35 import sun.net.ext.ExtendedSocketOptions; 36 import sun.security.action.GetPropertyAction; 37 38 /** 39 * Abstract datagram and multicast socket implementation base class. 40 * Note: This is not a public class, so that applets cannot call 41 * into the implementation directly and hence cannot bypass the 42 * security checks present in the DatagramSocket and MulticastSocket 43 * classes. 44 * 45 * @author Pavani Diwanji 46 */ 47 48 abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl 49 { 50 /* timeout value for receive() */ 51 int timeout = 0; 52 boolean connected = false; 53 private int trafficClass = 0; 54 protected InetAddress connectedAddress = null; 55 private int connectedPort = -1; 56 57 private static final String os = 58 GetPropertyAction.privilegedGetProperty("os.name"); 59 60 /** 61 * flag set if the native connect() call not to be used 62 */ 63 private static final boolean connectDisabled = os.contains("OS X"); 64 65 /** 66 * Load net library into runtime. 67 */ 68 static { 69 java.security.AccessController.doPrivileged( 70 new java.security.PrivilegedAction<>() { 71 public Void run() { 72 System.loadLibrary("net"); 73 return null; 74 } 75 }); 76 } 77 78 private static volatile boolean checkedReusePort; 79 private static volatile boolean isReusePortAvailable; 80 81 /** 82 * Tells whether SO_REUSEPORT is supported. 83 */ 84 static boolean isReusePortAvailable() { 85 if (!checkedReusePort) { 86 isReusePortAvailable = isReusePortAvailable0(); 87 checkedReusePort = true; 88 } 89 return isReusePortAvailable; 90 } 91 92 /** 93 * Creates a datagram socket 94 */ 95 protected synchronized void create() throws SocketException { 96 ResourceManager.beforeUdpCreate(); 97 fd = new FileDescriptor(); 98 try { 99 datagramSocketCreate(); 100 SocketCleanable.register(fd); 101 } catch (SocketException ioe) { 102 ResourceManager.afterUdpClose(); 103 fd = null; 104 throw ioe; 105 } 106 } 107 108 /** 109 * Binds a datagram socket to a local port. 110 */ 111 protected synchronized void bind(int lport, InetAddress laddr) 112 throws SocketException { 113 bind0(lport, laddr); 114 } 115 116 protected abstract void bind0(int lport, InetAddress laddr) 117 throws SocketException; 118 119 /** 120 * Sends a datagram packet. The packet contains the data and the 121 * destination address to send the packet to. 122 * @param p the packet to be sent. 123 */ 124 protected abstract void send(DatagramPacket p) throws IOException; 125 126 /** 127 * Connects a datagram socket to a remote destination. This associates the remote 128 * address with the local socket so that datagrams may only be sent to this destination 129 * and received from this destination. 130 * @param address the remote InetAddress to connect to 131 * @param port the remote port number 132 */ 133 protected void connect(InetAddress address, int port) throws SocketException { 134 connect0(address, port); 135 connectedAddress = address; 136 connectedPort = port; 137 connected = true; 138 } 139 140 /** 141 * Disconnects a previously connected socket. Does nothing if the socket was 142 * not connected already. 143 */ 144 protected void disconnect() { 145 disconnect0(connectedAddress.holder().getFamily()); 146 connected = false; 147 connectedAddress = null; 148 connectedPort = -1; 149 } 150 151 /** 152 * Peek at the packet to see who it is from. 153 * @param i the address to populate with the sender address 154 */ 155 protected abstract int peek(InetAddress i) throws IOException; 156 protected abstract int peekData(DatagramPacket p) throws IOException; 157 /** 158 * Receive the datagram packet. 159 * @param p the packet to receive into 160 */ 161 protected synchronized void receive(DatagramPacket p) 162 throws IOException { 163 receive0(p); 164 } 165 166 protected abstract void receive0(DatagramPacket p) 167 throws IOException; 168 169 /** 170 * Set the TTL (time-to-live) option. 171 * @param ttl TTL to be set. 172 */ 173 protected abstract void setTimeToLive(int ttl) throws IOException; 174 175 /** 176 * Get the TTL (time-to-live) option. 177 */ 178 protected abstract int getTimeToLive() throws IOException; 179 180 /** 181 * Set the TTL (time-to-live) option. 182 * @param ttl TTL to be set. 183 */ 184 @Deprecated 185 protected abstract void setTTL(byte ttl) throws IOException; 186 187 /** 188 * Get the TTL (time-to-live) option. 189 */ 190 @Deprecated 191 protected abstract byte getTTL() throws IOException; 192 193 /** 194 * Join the multicast group. 195 * @param inetaddr multicast address to join. 196 */ 197 protected void join(InetAddress inetaddr) throws IOException { 198 join(inetaddr, null); 199 } 200 201 /** 202 * Leave the multicast group. 203 * @param inetaddr multicast address to leave. 204 */ 205 protected void leave(InetAddress inetaddr) throws IOException { 206 leave(inetaddr, null); 207 } 208 /** 209 * Join the multicast group. 210 * @param mcastaddr multicast address to join. 211 * @param netIf specifies the local interface to receive multicast 212 * datagram packets 213 * @throws IllegalArgumentException if mcastaddr is null or is a 214 * SocketAddress subclass not supported by this socket 215 * @since 1.4 216 */ 217 218 protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) 219 throws IOException { 220 if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) 221 throw new IllegalArgumentException("Unsupported address type"); 222 join(((InetSocketAddress)mcastaddr).getAddress(), netIf); 223 } 224 225 protected abstract void join(InetAddress inetaddr, NetworkInterface netIf) 226 throws IOException; 227 228 /** 229 * Leave the multicast group. 230 * @param mcastaddr multicast address to leave. 231 * @param netIf specified the local interface to leave the group at 232 * @throws IllegalArgumentException if mcastaddr is null or is a 233 * SocketAddress subclass not supported by this socket 234 * @since 1.4 235 */ 236 protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) 237 throws IOException { 238 if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) 239 throw new IllegalArgumentException("Unsupported address type"); 240 leave(((InetSocketAddress)mcastaddr).getAddress(), netIf); 241 } 242 243 protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf) 244 throws IOException; 245 246 /** 247 * Close the socket. 248 */ 249 protected void close() { 250 if (fd != null) { 251 SocketCleanable.unregister(fd); 252 datagramSocketClose(); 253 ResourceManager.afterUdpClose(); 254 fd = null; 255 } 256 } 257 258 protected boolean isClosed() { 259 return (fd == null) ? true : false; 260 } 261 262 /** 263 * set a value - since we only support (setting) binary options 264 * here, o must be a Boolean 265 */ 266 267 public void setOption(int optID, Object o) throws SocketException { 268 if (isClosed()) { 269 throw new SocketException("Socket Closed"); 270 } 271 switch (optID) { 272 /* check type safety b4 going native. These should never 273 * fail, since only java.Socket* has access to 274 * PlainSocketImpl.setOption(). 275 */ 276 case SO_TIMEOUT: 277 if (o == null || !(o instanceof Integer)) { 278 throw new SocketException("bad argument for SO_TIMEOUT"); 279 } 280 int tmp = ((Integer) o).intValue(); 281 if (tmp < 0) 282 throw new IllegalArgumentException("timeout < 0"); 283 timeout = tmp; 284 return; 285 case IP_TOS: 286 if (o == null || !(o instanceof Integer)) { 287 throw new SocketException("bad argument for IP_TOS"); 288 } 289 trafficClass = ((Integer)o).intValue(); 290 break; 291 case SO_REUSEADDR: 292 if (o == null || !(o instanceof Boolean)) { 293 throw new SocketException("bad argument for SO_REUSEADDR"); 294 } 295 break; 296 case SO_BROADCAST: 297 if (o == null || !(o instanceof Boolean)) { 298 throw new SocketException("bad argument for SO_BROADCAST"); 299 } 300 break; 301 case SO_BINDADDR: 302 throw new SocketException("Cannot re-bind Socket"); 303 case SO_RCVBUF: 304 case SO_SNDBUF: 305 if (o == null || !(o instanceof Integer) || 306 ((Integer)o).intValue() < 0) { 307 throw new SocketException("bad argument for SO_SNDBUF or " + 308 "SO_RCVBUF"); 309 } 310 break; 311 case IP_MULTICAST_IF: 312 if (o == null || !(o instanceof InetAddress)) 313 throw new SocketException("bad argument for IP_MULTICAST_IF"); 314 break; 315 case IP_MULTICAST_IF2: 316 if (o == null || !(o instanceof NetworkInterface)) 317 throw new SocketException("bad argument for IP_MULTICAST_IF2"); 318 break; 319 case IP_MULTICAST_LOOP: 320 if (o == null || !(o instanceof Boolean)) 321 throw new SocketException("bad argument for IP_MULTICAST_LOOP"); 322 break; 323 case SO_REUSEPORT: 324 if (o == null || !(o instanceof Boolean)) { 325 throw new SocketException("bad argument for SO_REUSEPORT"); 326 } 327 if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { 328 throw new UnsupportedOperationException("unsupported option"); 329 } 330 break; 331 default: 332 throw new SocketException("invalid option: " + optID); 333 } 334 socketSetOption(optID, o); 335 } 336 337 /* 338 * get option's state - set or not 339 */ 340 341 public Object getOption(int optID) throws SocketException { 342 if (isClosed()) { 343 throw new SocketException("Socket Closed"); 344 } 345 346 Object result; 347 348 switch (optID) { 349 case SO_TIMEOUT: 350 result = timeout; 351 break; 352 353 case IP_TOS: 354 result = socketGetOption(optID); 355 if ( ((Integer)result).intValue() == -1) { 356 result = trafficClass; 357 } 358 break; 359 360 case SO_BINDADDR: 361 case IP_MULTICAST_IF: 362 case IP_MULTICAST_IF2: 363 case SO_RCVBUF: 364 case SO_SNDBUF: 365 case IP_MULTICAST_LOOP: 366 case SO_REUSEADDR: 367 case SO_BROADCAST: 368 result = socketGetOption(optID); 369 break; 370 371 case SO_REUSEPORT: 372 if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { 373 throw new UnsupportedOperationException("unsupported option"); 374 } 375 result = socketGetOption(optID); 376 break; 377 378 default: 379 throw new SocketException("invalid option: " + optID); 380 } 381 382 return result; 383 } 384 385 static final ExtendedSocketOptions extendedOptions = 386 ExtendedSocketOptions.getInstance(); 387 388 private static final Set<SocketOption<?>> datagramSocketOptions = datagramSocketOptions(); 389 private static final Set<SocketOption<?>> multicastSocketOptions = multicastSocketOptions(); 390 391 private static Set<SocketOption<?>> datagramSocketOptions() { 392 HashSet<SocketOption<?>> options = new HashSet<>(); 393 options.add(StandardSocketOptions.SO_SNDBUF); 394 options.add(StandardSocketOptions.SO_RCVBUF); 395 options.add(StandardSocketOptions.SO_REUSEADDR); 396 options.add(StandardSocketOptions.IP_TOS); 397 if (isReusePortAvailable()) 398 options.add(StandardSocketOptions.SO_REUSEPORT); 399 options.addAll(ExtendedSocketOptions.datagramSocketOptions()); 400 return Collections.unmodifiableSet(options); 401 } 402 403 private static Set<SocketOption<?>> multicastSocketOptions() { 404 HashSet<SocketOption<?>> options = new HashSet<>(); 405 options.add(StandardSocketOptions.SO_SNDBUF); 406 options.add(StandardSocketOptions.SO_RCVBUF); 407 options.add(StandardSocketOptions.SO_REUSEADDR); 408 options.add(StandardSocketOptions.IP_TOS); 409 options.add(StandardSocketOptions.IP_MULTICAST_IF); 410 options.add(StandardSocketOptions.IP_MULTICAST_TTL); 411 options.add(StandardSocketOptions.IP_MULTICAST_LOOP); 412 if (isReusePortAvailable()) 413 options.add(StandardSocketOptions.SO_REUSEPORT); 414 options.addAll(ExtendedSocketOptions.datagramSocketOptions()); 415 return Collections.unmodifiableSet(options); 416 } 417 418 @Override 419 protected Set<SocketOption<?>> supportedOptions() { 420 if (getDatagramSocket() instanceof MulticastSocket) 421 return multicastSocketOptions; 422 else 423 return datagramSocketOptions; 424 } 425 426 @Override 427 protected <T> void setOption(SocketOption<T> name, T value) throws IOException { 428 Objects.requireNonNull(name); 429 if (!supportedOptions().contains(name)) 430 throw new UnsupportedOperationException("'" + name + "' not supported"); 431 432 if (!name.type().isInstance(value)) 433 throw new IllegalArgumentException("Invalid value '" + value + "'"); 434 435 if (isClosed()) 436 throw new SocketException("Socket closed"); 437 438 if (name == StandardSocketOptions.SO_SNDBUF) { 439 if (((Integer)value).intValue() < 0) 440 throw new IllegalArgumentException("Invalid send buffer size:" + value); 441 setOption(SocketOptions.SO_SNDBUF, value); 442 } else if (name == StandardSocketOptions.SO_RCVBUF) { 443 if (((Integer)value).intValue() < 0) 444 throw new IllegalArgumentException("Invalid recv buffer size:" + value); 445 setOption(SocketOptions.SO_RCVBUF, value); 446 } else if (name == StandardSocketOptions.SO_REUSEADDR) { 447 setOption(SocketOptions.SO_REUSEADDR, value); 448 } else if (name == StandardSocketOptions.SO_REUSEPORT) { 449 setOption(SocketOptions.SO_REUSEPORT, value); 450 } else if (name == StandardSocketOptions.IP_TOS) { 451 int i = ((Integer)value).intValue(); 452 if (i < 0 || i > 255) 453 throw new IllegalArgumentException("Invalid IP_TOS value: " + value); 454 setOption(SocketOptions.IP_TOS, value); 455 } else if (name == StandardSocketOptions.IP_MULTICAST_IF ) { 456 setOption(SocketOptions.IP_MULTICAST_IF2, value); 457 } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) { 458 int i = ((Integer)value).intValue(); 459 if (i < 0 || i > 255) 460 throw new IllegalArgumentException("Invalid TTL/hop value: " + value); 461 setTimeToLive((Integer)value); 462 } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) { 463 setOption(SocketOptions.IP_MULTICAST_LOOP, value); 464 } else if (extendedOptions.isOptionSupported(name)) { 465 extendedOptions.setOption(fd, name, value); 466 } else { 467 throw new AssertionError("unknown option :" + name); 468 } 469 } 470 471 @Override 472 @SuppressWarnings("unchecked") 473 protected <T> T getOption(SocketOption<T> name) throws IOException { 474 Objects.requireNonNull(name); 475 if (!supportedOptions().contains(name)) 476 throw new UnsupportedOperationException("'" + name + "' not supported"); 477 478 if (isClosed()) 479 throw new SocketException("Socket closed"); 480 481 if (name == StandardSocketOptions.SO_SNDBUF) { 482 return (T) getOption(SocketOptions.SO_SNDBUF); 483 } else if (name == StandardSocketOptions.SO_RCVBUF) { 484 return (T) getOption(SocketOptions.SO_RCVBUF); 485 } else if (name == StandardSocketOptions.SO_REUSEADDR) { 486 return (T) getOption(SocketOptions.SO_REUSEADDR); 487 } else if (name == StandardSocketOptions.SO_REUSEPORT) { 488 return (T) getOption(SocketOptions.SO_REUSEPORT); 489 } else if (name == StandardSocketOptions.IP_TOS) { 490 return (T) getOption(SocketOptions.IP_TOS); 491 } else if (name == StandardSocketOptions.IP_MULTICAST_IF) { 492 return (T) getOption(SocketOptions.IP_MULTICAST_IF2); 493 } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) { 494 return (T) ((Integer) getTimeToLive()); 495 } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) { 496 return (T) getOption(SocketOptions.IP_MULTICAST_LOOP); 497 } else if (extendedOptions.isOptionSupported(name)) { 498 return (T) extendedOptions.getOption(fd, name); 499 } else { 500 throw new AssertionError("unknown option: " + name); 501 } 502 } 503 504 protected abstract void datagramSocketCreate() throws SocketException; 505 protected abstract void datagramSocketClose(); 506 protected abstract void socketSetOption(int opt, Object val) 507 throws SocketException; 508 protected abstract Object socketGetOption(int opt) throws SocketException; 509 510 protected abstract void connect0(InetAddress address, int port) throws SocketException; 511 protected abstract void disconnect0(int family); 512 513 protected boolean nativeConnectDisabled() { 514 return connectDisabled; 515 } 516 517 abstract int dataAvailable(); 518 private static native boolean isReusePortAvailable0(); 519 }