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 }