1 /*
   2  * Copyright (c) 2009, 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 sun.nio.ch.sctp;
  26 
  27 import java.io.FileDescriptor;
  28 import java.io.IOException;
  29 import java.net.InetAddress;
  30 import java.net.InetSocketAddress;
  31 import java.net.SocketAddress;
  32 import java.nio.channels.AlreadyBoundException;
  33 import java.util.Set;
  34 import java.util.HashSet;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import sun.net.util.IPAddressUtil;
  38 import sun.nio.ch.IOUtil;
  39 import sun.nio.ch.Net;
  40 import com.sun.nio.sctp.SctpSocketOption;
  41 import static com.sun.nio.sctp.SctpStandardSocketOptions.*;
  42 
  43 public class SctpNet {
  44     private static final String osName = AccessController.doPrivileged(
  45         (PrivilegedAction<String>) () -> System.getProperty("os.name"));
  46 
  47     /* -- Miscellaneous SCTP utilities -- */
  48 
  49     private static boolean IPv4MappedAddresses() {
  50         if ("SunOS".equals(osName)) {
  51             /* Solaris supports IPv4Mapped Addresses with bindx */
  52             return true;
  53         } /* else {  //other OS/implementations  */
  54 
  55         /* lksctp/linux requires Ipv4 addresses */
  56         return false;
  57     }
  58 
  59     static boolean throwAlreadyBoundException() throws IOException {
  60         throw new AlreadyBoundException();
  61     }
  62 
  63     static void listen(int fd, int backlog) throws IOException {
  64         listen0(fd, backlog);
  65     }
  66 
  67     static int connect(int fd, InetAddress remote, int remotePort)
  68             throws IOException {
  69         return connect0(fd, remote, remotePort);
  70     }
  71 
  72     static void close(int fd) throws IOException {
  73         close0(fd);
  74     }
  75 
  76     static void preClose(int fd) throws IOException {
  77         preClose0(fd);
  78     }
  79 
  80     /**
  81      * @param  oneToOne
  82      *         if {@code true} returns a one-to-one sctp socket, otherwise
  83      *         returns a one-to-many sctp socket
  84      */
  85     static FileDescriptor socket(boolean oneToOne) throws IOException {
  86         int nativefd = socket0(oneToOne);
  87         return IOUtil.newFD(nativefd);
  88     }
  89 
  90     static void bindx(int fd, InetAddress[] addrs, int port, boolean add)
  91             throws IOException {
  92         bindx(fd, addrs, port, addrs.length, add,
  93                 IPv4MappedAddresses());
  94     }
  95 
  96     static Set<SocketAddress> getLocalAddresses(int fd)
  97             throws IOException {
  98         Set<SocketAddress> set = null;
  99         SocketAddress[] saa = getLocalAddresses0(fd);
 100 
 101         if (saa != null) {
 102             set = getRevealedLocalAddressSet(saa);
 103         }
 104 
 105         return set;
 106     }
 107 
 108     private static Set<SocketAddress> getRevealedLocalAddressSet(
 109             SocketAddress[] saa)
 110     {
 111          SecurityManager sm = System.getSecurityManager();
 112          Set<SocketAddress> set = new HashSet<>(saa.length);
 113          for (SocketAddress sa : saa) {
 114              set.add(getRevealedLocalAddress(sa, sm));
 115          }
 116          return set;
 117     }
 118 
 119     private static SocketAddress getRevealedLocalAddress(SocketAddress sa,
 120                                                          SecurityManager sm)
 121     {
 122         if (sm == null || sa == null)
 123             return sa;
 124         InetSocketAddress ia = (InetSocketAddress)sa;
 125         try{
 126             sm.checkConnect(ia.getAddress().getHostAddress(), -1);
 127             // Security check passed
 128         } catch (SecurityException e) {
 129             // Return loopback address
 130             return new InetSocketAddress(InetAddress.getLoopbackAddress(),
 131                                          ia.getPort());
 132         }
 133         return sa;
 134     }
 135 
 136     static Set<SocketAddress> getRemoteAddresses(int fd, int assocId)
 137             throws IOException {
 138         HashSet<SocketAddress> set = null;
 139         SocketAddress[] saa = getRemoteAddresses0(fd, assocId);
 140 
 141         if (saa != null) {
 142             set = new HashSet<SocketAddress>(saa.length);
 143             for (SocketAddress sa : saa)
 144                 set.add(sa);
 145         }
 146 
 147         return set;
 148     }
 149 
 150     static <T> void setSocketOption(int fd,
 151                                     SctpSocketOption<T> name,
 152                                     T value,
 153                                     int assocId)
 154             throws IOException {
 155         if (value == null)
 156             throw new IllegalArgumentException("Invalid option value");
 157 
 158         if (name.equals(SCTP_INIT_MAXSTREAMS)) {
 159             InitMaxStreams maxStreamValue = (InitMaxStreams)value;
 160             SctpNet.setInitMsgOption0(fd,
 161                  maxStreamValue.maxInStreams(), maxStreamValue.maxOutStreams());
 162         } else if (name.equals(SCTP_PRIMARY_ADDR) ||
 163                    name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
 164 
 165             SocketAddress addr  = (SocketAddress) value;
 166             if (addr == null)
 167                 throw new IllegalArgumentException("Invalid option value");
 168 
 169             Net.checkAddress(addr);
 170             InetSocketAddress netAddr = (InetSocketAddress)addr;
 171 
 172             if (name.equals(SCTP_PRIMARY_ADDR)) {
 173                 InetAddress inetAddress = netAddr.getAddress();
 174                 if (inetAddress.isLinkLocalAddress()) {
 175                     inetAddress = IPAddressUtil.toScopedAddress(inetAddress);
 176                 }
 177                 setPrimAddrOption0(fd,
 178                                    assocId,
 179                                    inetAddress,
 180                                    netAddr.getPort());
 181             } else {
 182                 setPeerPrimAddrOption0(fd,
 183                                        assocId,
 184                                        netAddr.getAddress(),
 185                                        netAddr.getPort(),
 186                                        IPv4MappedAddresses());
 187             }
 188         } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
 189             name.equals(SCTP_EXPLICIT_COMPLETE) ||
 190             name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
 191             name.equals(SCTP_NODELAY) ||
 192             name.equals(SO_SNDBUF) ||
 193             name.equals(SO_RCVBUF) ||
 194             name.equals(SO_LINGER)) {
 195             setIntOption(fd, name, value);
 196         } else {
 197             throw new AssertionError("Unknown socket option");
 198         }
 199     }
 200 
 201     static Object getSocketOption(int fd, SctpSocketOption<?> name, int assocId)
 202              throws IOException {
 203          if (name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
 204             throw new IllegalArgumentException(
 205                     "SCTP_SET_PEER_PRIMARY_ADDR cannot be retrieved");
 206         } else if (name.equals(SCTP_INIT_MAXSTREAMS)) {
 207             /* container for holding maxIn/Out streams */
 208             int[] values = new int[2];
 209             SctpNet.getInitMsgOption0(fd, values);
 210             return InitMaxStreams.create(values[0], values[1]);
 211         } else if (name.equals(SCTP_PRIMARY_ADDR)) {
 212             return getPrimAddrOption0(fd, assocId);
 213         } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
 214             name.equals(SCTP_EXPLICIT_COMPLETE) ||
 215             name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
 216             name.equals(SCTP_NODELAY) ||
 217             name.equals(SO_SNDBUF) ||
 218             name.equals(SO_RCVBUF) ||
 219             name.equals(SO_LINGER)) {
 220             return getIntOption(fd, name);
 221         } else {
 222             throw new AssertionError("Unknown socket option");
 223         }
 224     }
 225 
 226     static void setIntOption(int fd, SctpSocketOption<?> name, Object value)
 227             throws IOException {
 228         if (value == null)
 229             throw new IllegalArgumentException("Invalid option value");
 230 
 231         Class<?> type = name.type();
 232         if (type != Integer.class && type != Boolean.class)
 233             throw new AssertionError("Should not reach here");
 234 
 235         if (name == SO_RCVBUF ||
 236             name == SO_SNDBUF)
 237         {
 238             int i = ((Integer)value).intValue();
 239             if (i < 0)
 240                 throw new IllegalArgumentException(
 241                         "Invalid send/receive buffer size");
 242         } else if (name == SO_LINGER) {
 243             int i = ((Integer)value).intValue();
 244             if (i < 0)
 245                 value = Integer.valueOf(-1);
 246             if (i > 65535)
 247                 value = Integer.valueOf(65535);
 248         } else if (name.equals(SCTP_FRAGMENT_INTERLEAVE)) {
 249             int i = ((Integer)value).intValue();
 250             if (i < 0 || i > 2)
 251                 throw new IllegalArgumentException(
 252                         "Invalid value for SCTP_FRAGMENT_INTERLEAVE");
 253         }
 254 
 255         int arg;
 256         if (type == Integer.class) {
 257             arg = ((Integer)value).intValue();
 258         } else {
 259             boolean b = ((Boolean)value).booleanValue();
 260             arg = (b) ? 1 : 0;
 261         }
 262 
 263         setIntOption0(fd, ((SctpStdSocketOption)name).constValue(), arg);
 264     }
 265 
 266     static Object getIntOption(int fd, SctpSocketOption<?> name)
 267             throws IOException {
 268         Class<?> type = name.type();
 269 
 270         if (type != Integer.class && type != Boolean.class)
 271             throw new AssertionError("Should not reach here");
 272 
 273         if (!(name instanceof SctpStdSocketOption))
 274             throw new AssertionError("Should not reach here");
 275 
 276         int value = getIntOption0(fd,
 277                 ((SctpStdSocketOption)name).constValue());
 278 
 279         if (type == Integer.class) {
 280             return Integer.valueOf(value);
 281         } else {
 282             return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
 283         }
 284     }
 285 
 286     static void shutdown(int fd, int assocId)
 287             throws IOException {
 288         shutdown0(fd, assocId);
 289     }
 290 
 291     static FileDescriptor branch(int fd, int assocId) throws IOException {
 292         int nativefd = branch0(fd, assocId);
 293         return IOUtil.newFD(nativefd);
 294     }
 295 
 296     /* Native Methods */
 297     static native int socket0(boolean oneToOne) throws IOException;
 298 
 299     static native void listen0(int fd, int backlog) throws IOException;
 300 
 301     static native int connect0(int fd, InetAddress remote, int remotePort)
 302         throws IOException;
 303 
 304     static native void close0(int fd) throws IOException;
 305 
 306     static native void preClose0(int fd) throws IOException;
 307 
 308     static native void bindx(int fd, InetAddress[] addrs, int port, int length,
 309             boolean add, boolean preferIPv6) throws IOException;
 310 
 311     static native int getIntOption0(int fd, int opt) throws IOException;
 312 
 313     static native void setIntOption0(int fd, int opt, int arg)
 314         throws IOException;
 315 
 316     static native SocketAddress[] getLocalAddresses0(int fd) throws IOException;
 317 
 318     static native SocketAddress[] getRemoteAddresses0(int fd, int assocId)
 319             throws IOException;
 320 
 321     static native int branch0(int fd, int assocId) throws IOException;
 322 
 323     static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia,
 324             int port) throws IOException;
 325 
 326     static native void setPeerPrimAddrOption0(int fd, int assocId,
 327             InetAddress ia, int port, boolean preferIPv6) throws IOException;
 328 
 329     static native SocketAddress getPrimAddrOption0(int fd, int assocId)
 330             throws IOException;
 331 
 332     /* retVals [0] maxInStreams, [1] maxOutStreams */
 333     static native void getInitMsgOption0(int fd, int[] retVals) throws IOException;
 334 
 335     static native void setInitMsgOption0(int fd, int arg1, int arg2)
 336             throws IOException;
 337 
 338     static native void shutdown0(int fd, int assocId);
 339 
 340     static native void init();
 341 
 342     static {
 343         IOUtil.load();   // loads nio & net native libraries
 344         java.security.AccessController.doPrivileged(
 345                 new java.security.PrivilegedAction<Void>() {
 346                     public Void run() {
 347                         System.loadLibrary("sctp");
 348                         return null;
 349                     }
 350                 });
 351         init();
 352     }
 353 }
 354