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