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