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