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