| /* VMPlainSocketImpl.java -- VM interface for default socket implementation | |
| Copyright (C) 2005, 2006 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.net; | |
| import java.io.IOException; | |
| import java.net.Inet4Address; | |
| import java.net.Inet6Address; | |
| import java.net.InetAddress; | |
| import java.net.InetSocketAddress; | |
| import java.net.NetworkInterface; | |
| import java.net.SocketException; | |
| import java.net.SocketOptions; | |
| import gnu.classpath.Configuration; | |
| import gnu.java.nio.VMChannel; | |
| /** | |
| * The VM interface for {@link gnu.java.net.PlainSocketImpl}. | |
| * | |
| * @author Ingo Proetel (proetel@aicas.com) | |
| * @author Roman Kennke (kennke@aicas.com) | |
| */ | |
| public final class VMPlainSocketImpl | |
| { | |
| /** Option id for time to live | |
| */ | |
| private static final int CP_IP_TTL = 0x1E61; | |
| private final State nfd; | |
| /** | |
| * Static initializer to load native library. | |
| */ | |
| static | |
| { | |
| if (Configuration.INIT_LOAD_LIBRARY) | |
| { | |
| System.loadLibrary("javanet"); | |
| } | |
| } | |
| public VMPlainSocketImpl() | |
| { | |
| // XXX consider adding security check here. | |
| nfd = new State(); | |
| } | |
| public VMPlainSocketImpl(VMChannel channel) throws IOException | |
| { | |
| this(); | |
| nfd.setChannelFD(channel.getState()); | |
| } | |
| public State getState() | |
| { | |
| return nfd; | |
| } | |
| /** This method exists to hide the CP_IP_TTL value from | |
| * higher levels. | |
| * | |
| * Always think of JNode ... :) | |
| */ | |
| public void setTimeToLive(int ttl) | |
| throws SocketException | |
| { | |
| try | |
| { | |
| setOption(nfd.getNativeFD(), CP_IP_TTL, ttl); | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| } | |
| public int getTimeToLive() | |
| throws SocketException | |
| { | |
| try | |
| { | |
| return getOption(nfd.getNativeFD(), CP_IP_TTL); | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| } | |
| public void setOption(int optionId, Object optionValue) | |
| throws SocketException | |
| { | |
| int value; | |
| if (optionValue instanceof Integer) | |
| value = ((Integer) optionValue).intValue(); | |
| else if (optionValue instanceof Boolean) | |
| // Switching off the linger behavior is done by setting | |
| // the value to -1. This is how the Java API interprets | |
| // it. | |
| value = ((Boolean) optionValue).booleanValue() | |
| ? 1 | |
| : (optionId == SocketOptions.SO_LINGER) | |
| ? -1 | |
| : 0; | |
| else | |
| throw new IllegalArgumentException("option value type " | |
| + optionValue.getClass().getName()); | |
| try | |
| { | |
| setOption(nfd.getNativeFD(), optionId, value); | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| } | |
| private static native void setOption(int fd, int id, int value) | |
| throws SocketException; | |
| public void setMulticastInterface(int optionId, InetAddress addr) | |
| throws SocketException | |
| { | |
| try | |
| { | |
| if (addr instanceof Inet4Address) | |
| setMulticastInterface(nfd.getNativeFD(), optionId, (Inet4Address) addr); | |
| else if (addr instanceof Inet6Address) | |
| { | |
| NetworkInterface iface = NetworkInterface.getByInetAddress(addr); | |
| setMulticastInterface6(nfd.getNativeFD(), optionId, iface.getName()); | |
| } | |
| else | |
| throw new SocketException("Unknown address format: " + addr); | |
| } | |
| catch (SocketException se) | |
| { | |
| throw se; | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| } | |
| private static native void setMulticastInterface(int fd, | |
| int optionId, | |
| Inet4Address addr); | |
| private static native void setMulticastInterface6(int fd, | |
| int optionId, | |
| String ifName); | |
| /** | |
| * Get a socket option. This implementation is only required to support | |
| * socket options that are boolean values, which include: | |
| * | |
| * SocketOptions.IP_MULTICAST_LOOP | |
| * SocketOptions.SO_BROADCAST | |
| * SocketOptions.SO_KEEPALIVE | |
| * SocketOptions.SO_OOBINLINE | |
| * SocketOptions.SO_REUSEADDR | |
| * SocketOptions.TCP_NODELAY | |
| * | |
| * and socket options that are integer values, which include: | |
| * | |
| * SocketOptions.IP_TOS | |
| * SocketOptions.SO_LINGER | |
| * SocketOptions.SO_RCVBUF | |
| * SocketOptions.SO_SNDBUF | |
| * SocketOptions.SO_TIMEOUT | |
| * | |
| * @param optionId The option ID to fetch. | |
| * @return A {@link Boolean} or {@link Integer} containing the socket | |
| * option. | |
| * @throws SocketException | |
| */ | |
| public Object getOption(int optionId) throws SocketException | |
| { | |
| int value; | |
| try | |
| { | |
| value = getOption(nfd.getNativeFD(), optionId); | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| switch (optionId) | |
| { | |
| case SocketOptions.IP_MULTICAST_LOOP: | |
| case SocketOptions.SO_BROADCAST: | |
| case SocketOptions.SO_KEEPALIVE: | |
| case SocketOptions.SO_OOBINLINE: | |
| case SocketOptions.SO_REUSEADDR: | |
| case SocketOptions.TCP_NODELAY: | |
| return Boolean.valueOf(value != 0); | |
| case SocketOptions.IP_TOS: | |
| case SocketOptions.SO_LINGER: | |
| case SocketOptions.SO_RCVBUF: | |
| case SocketOptions.SO_SNDBUF: | |
| case SocketOptions.SO_TIMEOUT: | |
| return new Integer(value); | |
| default: | |
| throw new SocketException("getting option " + optionId + | |
| " not supported here"); | |
| } | |
| } | |
| private static native int getOption(int fd, int id) throws SocketException; | |
| /** | |
| * Returns an Inet4Address or Inet6Address instance belonging to the | |
| * interface which is set as the multicast interface. | |
| * | |
| * The optionId is provided to make it possible that the native | |
| * implementation may do something different depending on whether | |
| * the value is SocketOptions.IP_MULTICAST_IF or | |
| * SocketOptions.IP_MULTICAST_IF2. | |
| */ | |
| public InetAddress getMulticastInterface(int optionId) | |
| throws SocketException | |
| { | |
| try | |
| { | |
| return getMulticastInterface(nfd.getNativeFD(), optionId); | |
| } | |
| catch (IOException ioe) | |
| { | |
| SocketException se = new SocketException(); | |
| se.initCause(ioe); | |
| throw se; | |
| } | |
| } | |
| private static native InetAddress getMulticastInterface(int fd, | |
| int optionId); | |
| /** | |
| * Binds this socket to the given local address and port. | |
| * | |
| * @param address The address to bind to; the InetAddress is either | |
| * an IPv4 or IPv6 address. | |
| * @throws IOException If binding fails; for example, if the port | |
| * in the given InetSocketAddress is privileged, and the current | |
| * process has insufficient privileges. | |
| */ | |
| public void bind(InetSocketAddress address) throws IOException | |
| { | |
| InetAddress addr = address.getAddress(); | |
| if (addr instanceof Inet4Address) | |
| { | |
| bind (nfd.getNativeFD(), addr.getAddress(), address.getPort()); | |
| } | |
| else if (addr instanceof Inet6Address) | |
| bind6 (nfd.getNativeFD(), addr.getAddress(), address.getPort()); | |
| else | |
| throw new SocketException ("unsupported address type"); | |
| } | |
| /** | |
| * Native bind function for IPv4 addresses. The addr array must be | |
| * exactly four bytes long. | |
| * | |
| * VMs without native support need not implement this. | |
| * | |
| * @param fd The native file descriptor integer. | |
| * @param addr The IPv4 address, in network byte order. | |
| * @param port The port to bind to. | |
| * @throws IOException | |
| */ | |
| private static native void bind(int fd, byte[] addr, int port) | |
| throws IOException; | |
| /** | |
| * Native bind function for IPv6 addresses. The addr array must be | |
| * exactly sixteen bytes long. | |
| * | |
| * VMs without native support need not implement this. | |
| * | |
| * @param fd The native file descriptor integer. | |
| * @param addr The IPv6 address, in network byte order. | |
| * @param port The port to bind to. | |
| * @throws IOException | |
| */ | |
| private static native void bind6(int fd, byte[] addr, int port) | |
| throws IOException; | |
| /** | |
| * Listen on this socket for incoming connections. | |
| * | |
| * @param backlog The backlog of connections. | |
| * @throws IOException If listening fails. | |
| * @see gnu.java.nio.VMChannel#accept() | |
| */ | |
| public void listen(int backlog) throws IOException | |
| { | |
| listen(nfd.getNativeFD(), backlog); | |
| } | |
| /** | |
| * Native listen function. VMs without native support need not implement | |
| * this. | |
| * | |
| * @param fd The file descriptor integer. | |
| * @param backlog The listen backlog size. | |
| * @throws IOException | |
| */ | |
| private static native void listen(int fd, int backlog) throws IOException; | |
| public void join(InetAddress group) throws IOException | |
| { | |
| if (group instanceof Inet4Address) | |
| join(nfd.getNativeFD(), group.getAddress()); | |
| else if (group instanceof Inet6Address) | |
| join6(nfd.getNativeFD(), group.getAddress()); | |
| else | |
| throw new IllegalArgumentException("unknown address type"); | |
| } | |
| private static native void join(int fd, byte[] addr) throws IOException; | |
| private static native void join6(int fd, byte[] addr) throws IOException; | |
| public void leave(InetAddress group) throws IOException | |
| { | |
| if (group instanceof Inet4Address) | |
| leave(nfd.getNativeFD(), group.getAddress()); | |
| else if (group instanceof Inet6Address) | |
| leave6(nfd.getNativeFD(), group.getAddress()); | |
| else | |
| throw new IllegalArgumentException("unknown address type"); | |
| } | |
| private static native void leave(int fd, byte[] addr) throws IOException; | |
| private static native void leave6(int fd, byte[] addr) throws IOException; | |
| public void joinGroup(InetSocketAddress addr, NetworkInterface netif) | |
| throws IOException | |
| { | |
| InetAddress address = addr.getAddress(); | |
| if (address instanceof Inet4Address) | |
| joinGroup(nfd.getNativeFD(), address.getAddress(), | |
| netif != null ? netif.getName() : null); | |
| else if (address instanceof Inet6Address) | |
| joinGroup6(nfd.getNativeFD(), address.getAddress(), | |
| netif != null ? netif.getName() : null); | |
| else | |
| throw new IllegalArgumentException("unknown address type"); | |
| } | |
| private static native void joinGroup(int fd, byte[] addr, String ifname) | |
| throws IOException; | |
| private static native void joinGroup6(int fd, byte[] addr, String ifname) | |
| throws IOException; | |
| public void leaveGroup(InetSocketAddress addr, NetworkInterface netif) | |
| throws IOException | |
| { | |
| InetAddress address = addr.getAddress(); | |
| if (address instanceof Inet4Address) | |
| leaveGroup(nfd.getNativeFD(), address.getAddress(), | |
| netif != null ? netif.getName() : null); | |
| else if (address instanceof Inet6Address) | |
| leaveGroup6(nfd.getNativeFD(), address.getAddress(), | |
| netif != null ? netif.getName() : null); | |
| else | |
| throw new IllegalArgumentException("unknown address type"); | |
| } | |
| private static native void leaveGroup(int fd, byte[] addr, String ifname) | |
| throws IOException; | |
| private static native void leaveGroup6(int fd, byte[] addr, String ifname) | |
| throws IOException; | |
| public void shutdownInput() throws IOException | |
| { | |
| shutdownInput(nfd.getNativeFD()); | |
| } | |
| private static native void shutdownInput(int native_fd) throws IOException; | |
| public void shutdownOutput() throws IOException | |
| { | |
| shutdownOutput(nfd.getNativeFD()); | |
| } | |
| private static native void shutdownOutput(int native_fd) throws IOException; | |
| public void sendUrgentData(int data) throws IOException | |
| { | |
| sendUrgentData(nfd.getNativeFD(), data); | |
| } | |
| private static native void sendUrgentData(int natfive_fd, int data) throws IOException; | |
| public void close() throws IOException | |
| { | |
| nfd.close(); | |
| } | |
| // Inner classes. | |
| /** | |
| * Our wrapper for the native file descriptor. In this implementation, | |
| * it is a simple wrapper around {@link VMChannel.State}, to simplify | |
| * management of the native state. | |
| */ | |
| public final class State | |
| { | |
| private VMChannel.State channelFd; | |
| State() | |
| { | |
| channelFd = null; | |
| } | |
| public boolean isValid() | |
| { | |
| if (channelFd != null) | |
| return channelFd.isValid(); | |
| return false; | |
| } | |
| public int getNativeFD() throws IOException | |
| { | |
| return channelFd.getNativeFD(); | |
| } | |
| public void setChannelFD(final VMChannel.State nfd) throws IOException | |
| { | |
| if (this.channelFd != null && this.channelFd.isValid()) | |
| throw new IOException("file descriptor already initialized"); | |
| this.channelFd = nfd; | |
| } | |
| public void close() throws IOException | |
| { | |
| if (channelFd == null) | |
| throw new IOException("invalid file descriptor"); | |
| channelFd.close(); | |
| } | |
| protected void finalize() throws Throwable | |
| { | |
| try | |
| { | |
| if (isValid()) | |
| close(); | |
| } | |
| finally | |
| { | |
| super.finalize(); | |
| } | |
| } | |
| } | |
| } |