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