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