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