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 }