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