diff options
Diffstat (limited to 'libjava/gnu/java')
-rw-r--r-- | libjava/gnu/java/net/PlainDatagramSocketImpl.java | 280 | ||||
-rw-r--r-- | libjava/gnu/java/net/PlainSocketImpl.java | 401 | ||||
-rw-r--r-- | libjava/gnu/java/net/SocketInputStream.java | 204 | ||||
-rw-r--r-- | libjava/gnu/java/net/SocketOutputStream.java | 166 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainDatagramSocketImplNoNet.cc | 119 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainDatagramSocketImplPosix.cc | 750 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainDatagramSocketImplWin32.cc | 704 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainSocketImplNoNet.cc | 128 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainSocketImplPosix.cc | 857 | ||||
-rw-r--r-- | libjava/gnu/java/net/natPlainSocketImplWin32.cc | 784 |
10 files changed, 4393 insertions, 0 deletions
diff --git a/libjava/gnu/java/net/PlainDatagramSocketImpl.java b/libjava/gnu/java/net/PlainDatagramSocketImpl.java new file mode 100644 index 0000000..a0785db --- /dev/null +++ b/libjava/gnu/java/net/PlainDatagramSocketImpl.java @@ -0,0 +1,280 @@ +/* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation + Copyright (C) 1998, 1999, 2001, 2003 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.DatagramPacket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketOptions; +import java.net.SocketException; +import gnu.classpath.Configuration; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This is the default socket implementation for datagram sockets. + * It makes native calls to C routines that implement BSD style + * SOCK_DGRAM sockets in the AF_INET family. + * + * @author Aaron M. Renn <arenn@urbanophile.com> + * @author Warren Levy <warrenl@cygnus.com> + */ +public final class PlainDatagramSocketImpl extends DatagramSocketImpl +{ + // Static initializer to load native library + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javanet"); + } + } + + // These fields are mirrored for use in native code to avoid cpp conflicts + // when the #defines in system header files are the same as the public fields. + static final int _Jv_TCP_NODELAY_ = SocketOptions.TCP_NODELAY, + _Jv_SO_BINDADDR_ = SocketOptions.SO_BINDADDR, + _Jv_SO_REUSEADDR_ = SocketOptions.SO_REUSEADDR, + _Jv_SO_BROADCAST_ = SocketOptions.SO_BROADCAST, + _Jv_SO_OOBINLINE_ = SocketOptions.SO_OOBINLINE, + _Jv_IP_MULTICAST_IF_ = SocketOptions.IP_MULTICAST_IF, + _Jv_IP_MULTICAST_IF2_ = SocketOptions.IP_MULTICAST_IF2, + _Jv_IP_MULTICAST_LOOP_ = SocketOptions.IP_MULTICAST_LOOP, + _Jv_IP_TOS_ = SocketOptions.IP_TOS, + _Jv_SO_LINGER_ = SocketOptions.SO_LINGER, + _Jv_SO_TIMEOUT_ = SocketOptions.SO_TIMEOUT, + _Jv_SO_SNDBUF_ = SocketOptions.SO_SNDBUF, + _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF, + _Jv_SO_KEEPALIVE_ = SocketOptions.SO_KEEPALIVE; + + /** + * This is the actual underlying file descriptor + */ + int fnum = -1; + + // FIXME: Is this necessary? Could it help w/ DatagramSocket.getLocalAddress? + // InetAddress address; + + // localAddress cache + InetAddress localAddress; + + // 'timeout' is set/read by setOption/getOption. + int timeout = 0; + + /** + * Default do nothing constructor + */ + public PlainDatagramSocketImpl() + { + } + + /** + * Binds this socket to a particular port and interface + * + * @param port The port to bind to + * @param addr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected native void bind(int lport, InetAddress laddr) + throws SocketException; + + protected native void connect (InetAddress i, int port) + throws SocketException; + + protected native void disconnect (); + + /** + * Creates a new datagram socket + * + * @exception SocketException If an error occurs + */ + protected native void create() throws SocketException; + + protected native int peek(InetAddress i) throws IOException; + + protected native int peekData (DatagramPacket dp) throws IOException; + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ + protected native void setTimeToLive(int ttl) throws IOException; + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ + protected native int getTimeToLive() throws IOException; + + /** + * Sends a packet of data to a remote host + * + * @param packet The packet to send + * + * @exception IOException If an error occurs + */ + protected native void send(DatagramPacket p) throws IOException; + + /** + * Receives a UDP packet from the network + * + * @param packet The packet to fill in with the data received + * + * @exception IOException IOException If an error occurs + */ + protected native void receive(DatagramPacket p) throws IOException; + + /** + * Sets the value of an option on the socket + * + * @param option_id The identifier of the option to set + * @param val The value of the option to set + * + * @exception SocketException If an error occurs + */ + public native void setOption(int optID, Object value) throws SocketException; + + /** + * Retrieves the value of an option on the socket + * + * @param option_id The identifier of the option to retrieve + * + * @return The value of the option + * + * @exception SocketException If an error occurs + */ + public native Object getOption(int optID) throws SocketException; + + private native void mcastGrp(InetAddress inetaddr, NetworkInterface netIf, + boolean join) throws IOException; + + /** + * Closes the socket + */ + protected native void close(); + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 + */ + protected byte getTTL() throws IOException + { + return (byte) getTimeToLive(); + } + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 + */ + protected void setTTL(byte ttl) throws IOException + { + setTimeToLive(((int) ttl) & 0xFF); + } + + /** + * Joins a multicast group + * + * @param addr The group to join + * + * @exception IOException If an error occurs + */ + protected void join(InetAddress inetaddr) throws IOException + { + mcastGrp(inetaddr, null, true); + } + + /** + * Leaves a multicast group + * + * @param addr The group to leave + * + * @exception IOException If an error occurs + */ + protected void leave(InetAddress inetaddr) throws IOException + { + mcastGrp(inetaddr, null, false); + } + + protected void joinGroup (SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + mcastGrp(((InetSocketAddress)mcastaddr).getAddress(), netIf, true); + } + + protected void leaveGroup (SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + mcastGrp(((InetSocketAddress)mcastaddr).getAddress(), netIf, false); + } + + protected void finalize() throws Throwable + { + synchronized (this) + { + if (fnum != -1) + close(); + } + super.finalize(); + } +} diff --git a/libjava/gnu/java/net/PlainSocketImpl.java b/libjava/gnu/java/net/PlainSocketImpl.java new file mode 100644 index 0000000..182f357f --- /dev/null +++ b/libjava/gnu/java/net/PlainSocketImpl.java @@ -0,0 +1,401 @@ +/* PlainSocketImpl.java -- Default socket implementation + Copyright (C) 1998, 1999, 2003 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.SocketOptions; +import gnu.classpath.Configuration; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * Unless the application installs its own SocketImplFactory, this is the + * default socket implemetation that will be used. It simply uses a + * combination of Java and native routines to implement standard BSD + * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM + * + * @author Per Bothner <bothner@cygnus.com> + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> + * @author Aaron M. Renn <arenn@urbanophile.com> + */ +public final class PlainSocketImpl extends SocketImpl +{ + // Static initializer to load native library. + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javanet"); + } + } + + // These fields are mirrored for use in native code to avoid cpp conflicts + // when the #defines in system header files are the same as the public fields. + static final int _Jv_TCP_NODELAY_ = SocketOptions.TCP_NODELAY, + _Jv_SO_BINDADDR_ = SocketOptions.SO_BINDADDR, + _Jv_SO_REUSEADDR_ = SocketOptions.SO_REUSEADDR, + _Jv_SO_BROADCAST_ = SocketOptions.SO_BROADCAST, + _Jv_SO_OOBINLINE_ = SocketOptions.SO_OOBINLINE, + _Jv_IP_MULTICAST_IF_ = SocketOptions.IP_MULTICAST_IF, + _Jv_IP_MULTICAST_IF2_ = SocketOptions.IP_MULTICAST_IF2, + _Jv_IP_MULTICAST_LOOP_ = SocketOptions.IP_MULTICAST_LOOP, + _Jv_IP_TOS_ = SocketOptions.IP_TOS, + _Jv_SO_LINGER_ = SocketOptions.SO_LINGER, + _Jv_SO_TIMEOUT_ = SocketOptions.SO_TIMEOUT, + _Jv_SO_SNDBUF_ = SocketOptions.SO_SNDBUF, + _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF, + _Jv_SO_KEEPALIVE_ = SocketOptions.SO_KEEPALIVE; + + /** + * The OS file handle representing the socket. + * This is used for reads and writes to/from the socket and + * to close it. + * + * When the socket is closed this is reset to -1. + */ + int fnum = -1; + + // This value is set/read by setOption/getOption. + int timeout = 0; + + // localAddress cache + InetAddress localAddress; + + /** + * A cached copy of the in stream for reading from the socket. + */ + private InputStream in; + + /** + * A cached copy of the out stream for writing to the socket. + */ + private OutputStream out; + + /** + * Default do nothing constructor + */ + public PlainSocketImpl() + { + } + + protected void finalize() throws Throwable + { + synchronized (this) + { + if (fnum != -1) + try + { + close(); + } + catch (IOException ex) + { + // ignore + } + } + super.finalize(); + } + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. The option_id parameter is one of the defined constants in + * this interface. + * + * @param option_id The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ + public native void setOption(int optID, Object value) throws SocketException; + + /** + * Returns the current setting of the specified option. The Object returned + * will be an Integer for options that have integer values. The option_id + * is one of the defined constants in this interface. + * + * @param option_id The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ + public native Object getOption(int optID) throws SocketException; + + public native void shutdownInput () throws IOException; + + public native void shutdownOutput () throws IOException; + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + */ + protected native void create (boolean stream) throws IOException; + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param hostname The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected void connect (String host, int port) throws IOException + { + connect (new InetSocketAddress (InetAddress.getByName(host), port), 0); + } + + /** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected void connect (InetAddress host, int port) throws IOException + { + connect (new InetSocketAddress (host, port), 0); + } + + protected native void connect (SocketAddress addr, int timeout) + throws IOException; + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. **** How bind to INADDR_ANY? **** + * + * @param addr The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ + protected native void bind (InetAddress host, int port) throws IOException; + + /** + * Starts listening for connections on a socket. The queuelen parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param queuelen The length of the pending connection queue + * + * @exception IOException If an error occurs + */ + protected native void listen (int backlog) throws IOException; + + private native void accept (PlainSocketImpl s) throws IOException; + + /** + * Accepts a new connection on this socket and returns in in the + * passed in SocketImpl. + * + * @param impl The SocketImpl object to accept this connection. + */ + protected void accept (SocketImpl s) throws IOException + { + accept((PlainSocketImpl) s); + } + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ + protected native int available() throws IOException; + + /** + * Closes the socket. This will cause any InputStream or OutputStream + * objects for this Socket to be closed as well. + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ + protected native void close () throws IOException; + + protected native void sendUrgentData(int data) + throws IOException; + + native int read() throws IOException; + + /** + * Internal method used by SocketInputStream for reading data from + * the connection. Reads up to len bytes of data into the buffer + * buf starting at offset bytes into the buffer. + * + * @return The actual number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs + */ + native int read(byte[] buffer, int offset, int count) + throws IOException; + + native void write(int c) throws IOException; + + /** + * Internal method used by SocketOuputStream for writing data to + * the connection. Writes up to len bytes of data from the buffer + * buf starting at offset bytes into the buffer. + * + * @exception IOException If an error occurs + */ + native void write(byte[] buffer, int offset, int count) + throws IOException; + + /** + * Returns an InputStream object for reading from this socket. This will + * be an instance of SocketInputStream. + * + * @return An input stream attached to the socket. + * + * @exception IOException If an error occurs + */ + protected synchronized InputStream getInputStream() throws IOException + { + if (in == null) + in = new SocketInputStream(); + + return in; + } + + /** + * Returns an OutputStream object for writing to this socket. This will + * be an instance of SocketOutputStream. + * + * @return An output stream attached to the socket. + * + * @exception IOException If an error occurs + */ + protected synchronized OutputStream getOutputStream() throws IOException + { + if (out == null) + out = new SocketOutputStream(); + + return out; + } + + /** + * A stream which reads from the socket implementation. + * + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> + */ + class SocketInputStream + extends InputStream + { + SocketInputStream() + { + } + + public final void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + public final int available() throws IOException + { + return PlainSocketImpl.this.available(); + } + + public final int read() throws IOException + { + return PlainSocketImpl.this.read(); + } + + public final int read(byte[] buffer, int offset, int length) + throws IOException + { + return PlainSocketImpl.this.read(buffer, offset, length); + } + + public final int read(byte[] buffer) + throws IOException + { + return PlainSocketImpl.this.read(buffer, 0, buffer.length); + } + } + + /** A stream which writes to the socket implementation. + * + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> + */ + class SocketOutputStream + extends OutputStream + { + public final void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + public final void write(int c) throws IOException + { + PlainSocketImpl.this.write(c); + } + + public final void write(byte[] buffer, int offset, int length) + throws IOException + { + PlainSocketImpl.this.write(buffer, offset, length); + } + + public final void write(byte[] buffer) + throws IOException + { + PlainSocketImpl.this.write(buffer, 0, buffer.length); + } + } +} diff --git a/libjava/gnu/java/net/SocketInputStream.java b/libjava/gnu/java/net/SocketInputStream.java new file mode 100644 index 0000000..90d703b --- /dev/null +++ b/libjava/gnu/java/net/SocketInputStream.java @@ -0,0 +1,204 @@ +/* SocketInputStream.java -- An InputStream for Sockets + Copyright (C) 1998, 2000, 2002 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.InputStream; +import java.io.IOException; + +/** + * This class contains an implementation of <code>InputStream</code> for + * sockets. It in an internal only class used by <code>PlainSocketImpl</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class SocketInputStream extends InputStream +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The PlainSocketImpl object this stream is associated with + */ +private PlainSocketImpl impl; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Builds an instance of this class from a PlainSocketImpl object + */ +protected +SocketInputStream(PlainSocketImpl impl) +{ + this.impl = impl; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the number of bytes available to be read before blocking + */ +public int +available() throws IOException +{ + return(impl.available()); +} + +/*************************************************************************/ + +/** + * Determines if "mark" functionality is supported on this stream. For + * sockets, this is always false. Note that the superclass default is + * false, but it is overridden out of safety concerns and/or paranoia. + */ +public boolean +markSupported() +{ + return(false); +} + +/*************************************************************************/ + +/** + * Do nothing mark method since we don't support this functionality. Again, + * overriding out of paranoia. + * + * @param readlimit In theory, the number of bytes we can read before the mark becomes invalid + */ +public void +mark(int readlimit) +{ +} + +/*************************************************************************/ + +/** + * Since we don't support mark, this method always throws an exception + * + * @exception IOException Everytime since we don't support this functionality + */ +public void +reset() throws IOException +{ + throw new IOException("Socket InputStreams do not support mark/reset"); +} + +/*************************************************************************/ + +/** + * This method not only closes the stream, it closes the underlying socket + * (and thus any connection) and invalidates any other Input/Output streams + * for the underlying impl object + */ +public void +close() throws IOException +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * Reads the next byte of data and returns it as an int. + * + * @return The byte read (as an int) or -1 if end of stream); + * + * @exception IOException If an error occurs. + */ +public int +read() throws IOException +{ + byte buf[] = new byte[1]; + + int bytes_read = read(buf, 0, buf.length); + + if (bytes_read != -1) + return(buf[0] & 0xFF); + else + return(-1); +} + +/*************************************************************************/ + +/** + * Reads up to buf.length bytes of data into the caller supplied buffer. + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf) throws IOException +{ + return(read(buf, 0, buf.length)); +} + +/*************************************************************************/ + +/** + * Reads up to len bytes of data into the caller supplied buffer starting + * at offset bytes from the start of the buffer + * + * @return The number of bytes actually read or -1 if end of stream + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf, int offset, int len) throws IOException +{ + int bytes_read = impl.read(buf, offset, len); + if (bytes_read == 0) + return(-1); + + return(bytes_read); +} + +} // class SocketInputStream + diff --git a/libjava/gnu/java/net/SocketOutputStream.java b/libjava/gnu/java/net/SocketOutputStream.java new file mode 100644 index 0000000..4d68e3c --- /dev/null +++ b/libjava/gnu/java/net/SocketOutputStream.java @@ -0,0 +1,166 @@ +/* SocketOutputStream.java -- OutputStream for PlainSocketImpl + Copyright (C) 1998,2000 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.OutputStream; +import java.io.IOException; + +/** + * This class is used internally by <code>PlainSocketImpl</code> to be the + * <code>OutputStream</code> subclass returned by its + * <code>getOutputStream method</code>. It expects only to be used in that + * context. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class SocketOutputStream extends OutputStream +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The PlainSocketImpl object this stream is associated with + */ +private PlainSocketImpl impl; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Build an instance of this class from a PlainSocketImpl object + */ +protected +SocketOutputStream(PlainSocketImpl impl) +{ + this.impl = impl; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method closes the stream and the underlying socket connection. This + * action also effectively closes any other InputStream or OutputStream + * object associated with the connection. + * + * @exception IOException If an error occurs + */ +public void +close() throws IOException +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * Hmmm, we don't seem to have a flush() method in Socket impl, so just + * return for now, but this might need to be looked at later. + * + * @exception IOException Can't happen + */ +public void +flush() throws IOException +{ + return; +} + +/*************************************************************************/ + +/** + * Writes a byte (passed in as an int) to the given output stream + * + * @param b The byte to write + * + * @exception IOException If an error occurs + */ +public void +write(int b) throws IOException +{ + byte buf[] = new byte[1]; + + Integer i = new Integer(b); + buf[0] = i.byteValue(); + + write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * Write an array of bytes to the output stream + * + * @param buf The array of bytes to write + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf) throws IOException +{ + write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * Writes len number of bytes from the array buf to the stream starting + * at offset bytes into the buffer. + * + * @param buf The buffer + * @param offset Offset into the buffer to start writing from + * @param len The number of bytes to write + */ +public void +write(byte[] buf, int offset, int len) throws IOException +{ + impl.write(buf, offset, len); +} + +} // class SocketOutputStream + diff --git a/libjava/gnu/java/net/natPlainDatagramSocketImplNoNet.cc b/libjava/gnu/java/net/natPlainDatagramSocketImplNoNet.cc new file mode 100644 index 0000000..7cb5a2e --- /dev/null +++ b/libjava/gnu/java/net/natPlainDatagramSocketImplNoNet.cc @@ -0,0 +1,119 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> + +#include <gnu/java/net/PlainDatagramSocketImpl.h> +#include <java/io/IOException.h> +#include <java/lang/Object.h> +#include <java/net/BindException.h> +#include <java/net/DatagramPacket.h> +#include <java/net/InetAddress.h> +#include <java/net/NetworkInterface.h> +#include <java/net/SocketException.h> + +void +gnu::java::net::PlainDatagramSocketImpl::create () +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("DatagramSocketImpl.create: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::bind (jint, ::java::net::InetAddress *) +{ + throw new ::java::net::BindException ( + JvNewStringLatin1 ("DatagramSocketImpl.bind: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::connect (::java::net::InetAddress *, jint) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("DatagramSocketImpl.connect: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::disconnect () +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("DatagramSocketImpl.disconnect: unimplemented")); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peek (::java::net::InetAddress *) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.peek: unimplemented")); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peekData (::java::net::DatagramPacket *) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.peekData: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::close () +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.close: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::send (::java::net::DatagramPacket *) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.send: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::receive (::java::net::DatagramPacket *) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.receive: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setTimeToLive (jint) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.setTimeToLive: unimplemented")); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::getTimeToLive () +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.getTimeToLive: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::mcastGrp (::java::net::InetAddress *, + ::java::net::NetworkInterface *, + jboolean) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("DatagramSocketImpl.mcastGrp: unimplemented")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setOption (jint, ::java::lang::Object *) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("DatagramSocketImpl.setOption: unimplemented")); +} + +::java::lang::Object * +gnu::java::net::PlainDatagramSocketImpl::getOption (jint) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("DatagramSocketImpl.getOption: unimplemented")); +} diff --git a/libjava/gnu/java/net/natPlainDatagramSocketImplPosix.cc b/libjava/gnu/java/net/natPlainDatagramSocketImplPosix.cc new file mode 100644 index 0000000..f1897b7 --- /dev/null +++ b/libjava/gnu/java/net/natPlainDatagramSocketImplPosix.cc @@ -0,0 +1,750 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#include <errno.h> +#include <string.h> + +#if HAVE_BSTRING_H +// Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 +#include <bstring.h> +#endif + +#include <gcj/cni.h> +#include <gnu/java/net/PlainDatagramSocketImpl.h> +#include <java/io/IOException.h> +#include <java/io/InterruptedIOException.h> +#include <java/net/BindException.h> +#include <java/net/SocketException.h> +#include <java/net/InetAddress.h> +#include <java/net/NetworkInterface.h> +#include <java/net/DatagramPacket.h> +#include <java/net/PortUnreachableException.h> +#include <java/lang/InternalError.h> +#include <java/lang/Object.h> +#include <java/lang/Boolean.h> +#include <java/lang/Integer.h> + +union SockAddr +{ + struct sockaddr_in address; +#ifdef HAVE_INET6 + struct sockaddr_in6 address6; +#endif +}; + +union McastReq +{ +#if HAVE_STRUCT_IP_MREQ + struct ip_mreq mreq; +#endif +#if HAVE_STRUCT_IPV6_MREQ + struct ipv6_mreq mreq6; +#endif +}; + +union InAddr +{ + struct in_addr addr; +#ifdef HAVE_INET6 + struct in6_addr addr6; +#endif +}; + + +// FIXME: routines here and/or in natPlainSocketImpl.cc could throw +// NoRouteToHostException; also consider UnknownHostException, ConnectException. + +void +gnu::java::net::PlainDatagramSocketImpl::create () +{ + int sock = _Jv_socket (AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) + { + char* strerr = strerror (errno); + throw new ::java::net::SocketException (JvNewStringUTF (strerr)); + } + + _Jv_platform_close_on_exec (sock); + + // We use fnum in place of fd here. From leaving fd null we avoid + // the double close problem in FileDescriptor.finalize. + fnum = sock; +} + +void +gnu::java::net::PlainDatagramSocketImpl::bind (jint lport, + ::java::net::InetAddress *host) +{ + union SockAddr u; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4. + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + + if (len == 4) + { + u.address.sin_family = AF_INET; + + if (host != NULL) + memcpy (&u.address.sin_addr, bytes, len); + else + u.address.sin_addr.s_addr = htonl (INADDR_ANY); + + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (lport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (lport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (_Jv_bind (fnum, ptr, len) == 0) + { + socklen_t addrlen = sizeof(u); + + if (lport != 0) + localPort = lport; + else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0) + localPort = ntohs (u.address.sin_port); + else + goto error; + + /* Allow broadcast by default. */ + int broadcast = 1; + if (::setsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &broadcast, + sizeof (broadcast)) != 0) + goto error; + + return; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::BindException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::connect (::java::net::InetAddress *, jint) +{ + throw new ::java::lang::InternalError (JvNewStringLatin1 ( + "PlainDatagramSocketImpl::connect: not implemented yet")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::disconnect () +{ + throw new ::java::lang::InternalError (JvNewStringLatin1 ( + "PlainDatagramSocketImpl::disconnect: not implemented yet")); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peek (::java::net::InetAddress *i) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + ssize_t retlen = + ::recvfrom (fnum, (char *) NULL, 0, MSG_PEEK, (sockaddr*) &u, + &addrlen); + if (retlen < 0) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + i->addr = raddr; + return rport; + error: + char* strerr = strerror (errno); + + if (errno == ECONNREFUSED) + throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr)); + + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peekData (::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyte *dbytes = elements (p->getData()); + ssize_t retlen = 0; + + // Do timeouts via select since SO_RCVTIMEO is not always available. + if (timeout > 0 && fnum >= 0 && fnum < FD_SETSIZE) + { + fd_set rset; + struct timeval tv; + FD_ZERO(&rset); + FD_SET(fnum, &rset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + int retval; + if ((retval = _Jv_select (fnum + 1, &rset, NULL, NULL, &tv)) < 0) + goto error; + else if (retval == 0) + throw new ::java::io::InterruptedIOException (); + } + + retlen = + ::recvfrom (fnum, (char *) dbytes, p->getLength(), MSG_PEEK, (sockaddr*) &u, + &addrlen); + if (retlen < 0) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + p->setAddress (new ::java::net::InetAddress (raddr, NULL)); + p->setPort (rport); + p->setLength ((jint) retlen); + return rport; + + error: + char* strerr = strerror (errno); + + if (errno == ECONNREFUSED) + throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr)); + + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +// Close(shutdown) the socket. +void +gnu::java::net::PlainDatagramSocketImpl::close () +{ + // Avoid races from asynchronous finalization. + JvSynchronize sync (this); + + // The method isn't declared to throw anything, so we disregard + // the return value. + _Jv_close (fnum); + fnum = -1; + timeout = 0; +} + +void +gnu::java::net::PlainDatagramSocketImpl::send (::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + jint rport = p->getPort(); + union SockAddr u; + jbyteArray haddress = p->getAddress()->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + jbyte *dbytes = elements (p->getData()); + if (len == 4) + { + u.address.sin_family = AF_INET; + memcpy (&u.address.sin_addr, bytes, len); + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (rport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (rport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::sendto (fnum, (char *) dbytes, p->getLength(), 0, ptr, len) >= 0) + return; + + char* strerr = strerror (errno); + + if (errno == ECONNREFUSED) + throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr)); + + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::receive (::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyte *dbytes = elements (p->getData()); + ssize_t retlen = 0; + + // Do timeouts via select since SO_RCVTIMEO is not always available. + if (timeout > 0 && fnum >= 0 && fnum < FD_SETSIZE) + { + fd_set rset; + struct timeval tv; + FD_ZERO(&rset); + FD_SET(fnum, &rset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + int retval; + if ((retval = _Jv_select (fnum + 1, &rset, NULL, NULL, &tv)) < 0) + goto error; + else if (retval == 0) + throw new ::java::io::InterruptedIOException (); + } + + retlen = + ::recvfrom (fnum, (char *) dbytes, p->getLength(), 0, (sockaddr*) &u, + &addrlen); + if (retlen < 0) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + p->setAddress (new ::java::net::InetAddress (raddr, NULL)); + p->setPort (rport); + p->setLength ((jint) retlen); + return; + + error: + char* strerr = strerror (errno); + + if (errno == ECONNREFUSED) + throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr)); + + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl) +{ + // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4. + char val = (char) ttl; + socklen_t val_len = sizeof(val); + + if (::setsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0) + return; + + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::getTimeToLive () +{ + // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4. + char val; + socklen_t val_len = sizeof(val); + + if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0) + return ((int) val) & 0xFF; + + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::mcastGrp (::java::net::InetAddress *inetaddr, + ::java::net::NetworkInterface *, + jboolean join) +{ + // FIXME: implement use of NetworkInterface + + union McastReq u; + jbyteArray haddress = inetaddr->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + int level, opname; + const char *ptr; + if (0) + ; +#if HAVE_STRUCT_IP_MREQ + else if (len == 4) + { + level = IPPROTO_IP; + opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + memcpy (&u.mreq.imr_multiaddr, bytes, len); + // FIXME: If a non-default interface is set, use it; see Stevens p. 501. + // Maybe not, see note in last paragraph at bottom of Stevens p. 497. + u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); + len = sizeof (struct ip_mreq); + ptr = (const char *) &u.mreq; + } +#endif +#if HAVE_STRUCT_IPV6_MREQ + else if (len == 16) + { + level = IPPROTO_IPV6; + + /* Prefer new RFC 2553 names. */ +#ifndef IPV6_JOIN_GROUP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif +#ifndef IPV6_LEAVE_GROUP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif + + opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP; + memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len); + // FIXME: If a non-default interface is set, use it; see Stevens p. 501. + // Maybe not, see note in last paragraph at bottom of Stevens p. 497. + u.mreq6.ipv6mr_interface = 0; + len = sizeof (struct ipv6_mreq); + ptr = (const char *) &u.mreq6; + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::setsockopt (fnum, level, opname, ptr, len) == 0) + return; + + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setOption (jint optID, + ::java::lang::Object *value) +{ + int val; + socklen_t val_len = sizeof (val); + + if (fnum < 0) + throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed")); + + if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$)) + { + ::java::lang::Boolean *boolobj = + static_cast< ::java::lang::Boolean *> (value); + val = boolobj->booleanValue() ? 1 : 0; + } + else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$)) + { + ::java::lang::Integer *intobj = + static_cast< ::java::lang::Integer *> (value); + val = (int) intobj->intValue(); + } + // Else assume value to be an InetAddress for use with IP_MULTICAST_IF. + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("TCP_NODELAY not valid for UDP")); + return; + case _Jv_SO_LINGER_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_LINGER not valid for UDP")); + return; + case _Jv_SO_KEEPALIVE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_KEEPALIVE not valid for UDP")); + return; + + case _Jv_SO_BROADCAST_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + val_len) != 0) + goto error; + break; + + case _Jv_SO_OOBINLINE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_OOBINLINE: not valid for UDP")); + break; + + case _Jv_SO_SNDBUF_ : + case _Jv_SO_RCVBUF_ : +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, val_len) != 0) + goto error; +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported")); +#endif + return; + case _Jv_SO_REUSEADDR_ : +#if defined(SO_REUSEADDR) + if (::setsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val, + val_len) != 0) + goto error; +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_REUSEADDR not supported")); +#endif + return; + case _Jv_SO_BINDADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_BINDADDR: read only option")); + return; + case _Jv_IP_MULTICAST_IF_ : + union InAddr u; + jbyteArray haddress; + jbyte *bytes; + int len; + int level, opname; + const char *ptr; + + haddress = ((::java::net::InetAddress *) value)->addr; + bytes = elements (haddress); + len = haddress->length; + if (len == 4) + { + level = IPPROTO_IP; + opname = IP_MULTICAST_IF; + memcpy (&u.addr, bytes, len); + len = sizeof (struct in_addr); + ptr = (const char *) &u.addr; + } +// Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF +#if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF) + else if (len == 16) + { + level = IPPROTO_IPV6; + opname = IPV6_MULTICAST_IF; + memcpy (&u.addr6, bytes, len); + len = sizeof (struct in6_addr); + ptr = (const char *) &u.addr6; + } +#endif + else + throw + new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::setsockopt (fnum, level, opname, ptr, len) != 0) + goto error; + return; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_LOOP: not yet implemented")); + break; + + case _Jv_IP_TOS_ : + if (::setsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + val_len) != 0) + goto error; + return; + + case _Jv_SO_TIMEOUT_ : + timeout = val; + return; + default : + errno = ENOPROTOOPT; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::SocketException (JvNewStringUTF (strerr)); +} + +::java::lang::Object * +gnu::java::net::PlainDatagramSocketImpl::getOption (jint optID) +{ + int val; + socklen_t val_len = sizeof(val); + union SockAddr u; + socklen_t addrlen = sizeof(u); + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("TCP_NODELAY not valid for UDP")); + break; + case _Jv_SO_LINGER_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_LINGER not valid for UDP")); + break; + case _Jv_SO_KEEPALIVE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_KEEPALIVE not valid for UDP")); + break; + + case _Jv_SO_BROADCAST_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); + + case _Jv_SO_OOBINLINE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_OOBINLINE not valid for UDP")); + break; + + case _Jv_SO_RCVBUF_ : + case _Jv_SO_SNDBUF_ : +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, &val_len) != 0) + goto error; + else + return new ::java::lang::Integer (val); +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported")); +#endif + break; + case _Jv_SO_BINDADDR_: + // cache the local address + if (localAddress == NULL) + { + jbyteArray laddr; + if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0) + goto error; + if (u.address.sin_family == AF_INET) + { + laddr = JvNewByteArray (4); + memcpy (elements (laddr), &u.address.sin_addr, 4); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + laddr = JvNewByteArray (16); + memcpy (elements (laddr), &u.address6.sin6_addr, 16); + } +#endif + else + throw new ::java::net::SocketException ( + JvNewStringUTF ("invalid family")); + localAddress = new ::java::net::InetAddress (laddr, NULL); + } + return localAddress; + break; + case _Jv_SO_REUSEADDR_ : +#if defined(SO_REUSEADDR) + if (::getsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_REUSEADDR not supported")); +#endif + break; + case _Jv_IP_MULTICAST_IF_ : +#ifdef HAVE_INET_NTOA + struct in_addr inaddr; + socklen_t inaddr_len; + char *bytes; + + inaddr_len = sizeof(inaddr); + if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr, + &inaddr_len) != 0) + goto error; + + bytes = inet_ntoa (inaddr); + + return ::java::net::InetAddress::getByName (JvNewStringLatin1 (bytes)); +#else + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()")); +#endif + break; + case _Jv_SO_TIMEOUT_ : + return new ::java::lang::Integer (timeout); + break; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_MULTICAST_LOOP, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); + + case _Jv_IP_TOS_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Integer (val); + + default : + errno = ENOPROTOOPT; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::SocketException (JvNewStringUTF (strerr)); +} diff --git a/libjava/gnu/java/net/natPlainDatagramSocketImplWin32.cc b/libjava/gnu/java/net/natPlainDatagramSocketImplWin32.cc new file mode 100644 index 0000000..ae4b092 --- /dev/null +++ b/libjava/gnu/java/net/natPlainDatagramSocketImplWin32.cc @@ -0,0 +1,704 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> +#include <string.h> + +#if HAVE_BSTRING_H +// Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 +#include <bstring.h> +#endif + +#include <gnu/java/net/PlainDatagramSocketImpl.h> +#include <java/io/IOException.h> +#include <java/io/InterruptedIOException.h> +#include <java/net/BindException.h> +#include <java/net/SocketException.h> +#include <java/net/InetAddress.h> +#include <java/net/NetworkInterface.h> +#include <java/net/DatagramPacket.h> +#include <java/net/PortUnreachableException.h> +#include <java/net/SocketTimeoutException.h> +#include <java/lang/InternalError.h> +#include <java/lang/Object.h> +#include <java/lang/Boolean.h> +#include <java/lang/Integer.h> + +union SockAddr +{ + struct sockaddr_in address; +#ifdef HAVE_INET6 + struct sockaddr_in6 address6; +#endif +}; + +union McastReq +{ +#if HAVE_STRUCT_IP_MREQ + struct ip_mreq mreq; +#endif +#if HAVE_STRUCT_IPV6_MREQ + struct ipv6_mreq mreq6; +#endif +}; + +union InAddr +{ + struct in_addr addr; +#ifdef HAVE_INET6 + struct in6_addr addr6; +#endif +}; + +// FIXME: routines here and/or in natPlainSocketImpl.cc could throw +// NoRouteToHostException; also consider UnknownHostException, ConnectException. + +void +gnu::java::net::PlainDatagramSocketImpl::create () +{ + SOCKET sock = ::socket (AF_INET, SOCK_DGRAM, 0); + + if (sock == INVALID_SOCKET) + { + _Jv_ThrowSocketException (); + } + + _Jv_platform_close_on_exec (sock); + + // We use fnum in place of fd here. From leaving fd null we avoid + // the double close problem in FileDescriptor.finalize. + fnum = (int) sock; +} + +void +gnu::java::net::PlainDatagramSocketImpl::bind (jint lport, + ::java::net::InetAddress *host) +{ + union SockAddr u; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4. + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + + if (len == 4) + { + u.address.sin_family = AF_INET; + + if (host != NULL) + memcpy (&u.address.sin_addr, bytes, len); + else + u.address.sin_addr.s_addr = htonl (INADDR_ANY); + + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (lport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (lport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::bind (fnum, ptr, len) == 0) + { + socklen_t addrlen = sizeof(u); + + if (lport != 0) + localPort = lport; + else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0) + localPort = ntohs (u.address.sin_port); + else + goto error; + + /* Allow broadcast by default. */ + int broadcast = 1; + if (::setsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &broadcast, + sizeof (broadcast)) != 0) + goto error; + + return; + } + +error: + DWORD dwErrorCode = WSAGetLastError (); + throw new ::java::net::BindException (_Jv_WinStrError (dwErrorCode)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::connect (::java::net::InetAddress *, jint) +{ + throw new ::java::lang::InternalError (JvNewStringLatin1 ( + "PlainDatagramSocketImpl::connect: not implemented yet")); +} + +void +gnu::java::net::PlainDatagramSocketImpl::disconnect () +{ + throw new ::java::lang::InternalError (JvNewStringLatin1 ( + "PlainDatagramSocketImpl::disconnect: not implemented yet")); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peek (::java::net::InetAddress *i) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + ssize_t retlen = + ::recvfrom (fnum, (char *) NULL, 0, MSG_PEEK, (sockaddr*) &u, + &addrlen); + if (retlen < 0) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + i->addr = raddr; + return rport; +error: + DWORD dwErrorCode = WSAGetLastError (); + if (dwErrorCode == WSAECONNRESET) + throw new ::java::net::PortUnreachableException (_Jv_WinStrError (dwErrorCode)); + + _Jv_ThrowIOException (); + return -1; + // we should never get here +} + +jint +gnu::java::net::PlainDatagramSocketImpl::peekData(::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyte *dbytes = elements (p->getData()); + ssize_t retlen = 0; + + if (timeout > 0) + { + int nRet= ::setsockopt(fnum, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)); + if (nRet != NO_ERROR) + goto error; + } + + retlen = + ::recvfrom (fnum, (char *) dbytes, p->getLength(), MSG_PEEK, (sockaddr*) &u, + &addrlen); + if (retlen == SOCKET_ERROR) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + p->setAddress (new InetAddress (raddr, NULL)); + p->setPort (rport); + p->setLength ((jint) retlen); + return rport; + +error: + DWORD dwErrorCode = WSAGetLastError (); + if (dwErrorCode == WSAECONNRESET) + throw new ::java::net::PortUnreachableException (_Jv_WinStrError (dwErrorCode)); + else if (dwErrorCode == WSAETIMEDOUT) + throw new ::java::net::SocketTimeoutException (_Jv_WinStrError (dwErrorCode)); + else + _Jv_ThrowIOException (); + + return -1; + // we should never get here +} + +// Close(shutdown) the socket. +void +gnu::java::net::PlainDatagramSocketImpl::close () +{ + // Avoid races from asynchronous finalization. + JvSynchronize sync (this); + + // The method isn't declared to throw anything, so we disregard + // the return value. + ::closesocket (fnum); + fnum = -1; + timeout = 0; +} + +void +gnu::java::net::PlainDatagramSocketImpl::send (::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + jint rport = p->getPort(); + union SockAddr u; + jbyteArray haddress = p->getAddress()->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + jbyte *dbytes = elements (p->getData()); + if (len == 4) + { + u.address.sin_family = AF_INET; + memcpy (&u.address.sin_addr, bytes, len); + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (rport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (rport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::sendto (fnum, (char *) dbytes, p->getLength(), 0, ptr, len) >= 0) + return; + + DWORD dwErrorCode = WSAGetLastError (); + if (dwErrorCode == WSAECONNRESET) + throw new ::java::net::PortUnreachableException (_Jv_WinStrError (dwErrorCode)); + + _Jv_ThrowIOException (); +} + +void +gnu::java::net::PlainDatagramSocketImpl::receive (::java::net::DatagramPacket *p) +{ + // FIXME: Deal with Multicast and if the socket is connected. + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyte *dbytes = elements (p->getData()); + ssize_t retlen = 0; + + if (timeout > 0) + { + // This implementation doesn't allow specifying an infinite + // timeout after specifying a finite one, but Sun's JDK 1.4.1 + // didn't seem to allow this either.... + int nRet= ::setsockopt(fnum, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)); + if (nRet != NO_ERROR) + goto error; + } + + retlen = + ::recvfrom (fnum, (char *) dbytes, p->getLength(), 0, (sockaddr*) &u, + &addrlen); + if (retlen < 0) + goto error; + // FIXME: Deal with Multicast addressing and if the socket is connected. + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + p->setAddress (new ::java::net::InetAddress (raddr, NULL)); + p->setPort (rport); + p->setLength ((jint) retlen); + return; + + error: + DWORD dwErrorCode = WSAGetLastError(); + if (dwErrorCode == WSAECONNRESET) + throw new ::java::net::PortUnreachableException (_Jv_WinStrError (dwErrorCode)); + else if (dwErrorCode == WSAETIMEDOUT) + throw new ::java::net::SocketTimeoutException (_Jv_WinStrError (dwErrorCode)); + else + throw new ::java::io::IOException (_Jv_WinStrError (dwErrorCode)); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl) +{ + // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4. + char val = (char) ttl; + socklen_t val_len = sizeof(val); + + if (::setsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0) + return; + + _Jv_ThrowIOException (); +} + +jint +gnu::java::net::PlainDatagramSocketImpl::getTimeToLive () +{ + // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4. + char val; + socklen_t val_len = sizeof(val); + + if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0) + return ((int) val) & 0xFF; + + _Jv_ThrowIOException (); + + return -1; + // we should never get here +} + +void +gnu::java::net::PlainDatagramSocketImpl::mcastGrp (::java::net::InetAddress *inetaddr, + ::java::net::NetworkInterface *, + jboolean join) +{ + // FIXME: implement use of NetworkInterface + jbyteArray haddress = inetaddr->addr; + int len = haddress->length; + int level, opname; + const char *ptr; + if (0) + ; +#if HAVE_STRUCT_IP_MREQ + else if (len == 4) + { + level = IPPROTO_IP; + opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + memcpy (&u.mreq.imr_multiaddr, bytes, len); + // FIXME: If a non-default interface is set, use it; see Stevens p. 501. + // Maybe not, see note in last paragraph at bottom of Stevens p. 497. + u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); + len = sizeof (struct ip_mreq); + ptr = (const char *) &u.mreq; + } +#endif +#if HAVE_STRUCT_IPV6_MREQ + else if (len == 16) + { + level = IPPROTO_IPV6; + + /* Prefer new RFC 2553 names. */ +#ifndef IPV6_JOIN_GROUP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif +#ifndef IPV6_LEAVE_GROUP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif + + opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP; + memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len); + // FIXME: If a non-default interface is set, use it; see Stevens p. 501. + // Maybe not, see note in last paragraph at bottom of Stevens p. 497. + u.mreq6.ipv6mr_interface = 0; + len = sizeof (struct ipv6_mreq); + ptr = (const char *) &u.mreq6; + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::setsockopt (fnum, level, opname, ptr, len) == 0) + return; + + _Jv_ThrowIOException (); +} + +void +gnu::java::net::PlainDatagramSocketImpl::setOption (jint optID, + ::java::lang::Object *value) +{ + int val; + socklen_t val_len = sizeof (val); + + if (fnum < 0) + throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed")); + + if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$)) + { + ::java::lang::Boolean *boolobj = + static_cast< ::java::lang::Boolean *> (value); + val = boolobj->booleanValue() ? 1 : 0; + } + else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$)) + { + ::java::lang::Integer *intobj = + static_cast< ::java::lang::Integer *> (value); + val = (int) intobj->intValue(); + } + // Else assume value to be an InetAddress for use with IP_MULTICAST_IF. + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("TCP_NODELAY not valid for UDP")); + return; + case _Jv_SO_LINGER_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_LINGER not valid for UDP")); + return; + case _Jv_SO_KEEPALIVE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_KEEPALIVE not valid for UDP")); + return; + + case _Jv_SO_BROADCAST_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + val_len) != 0) + goto error; + break; + + case _Jv_SO_OOBINLINE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_OOBINLINE: not valid for UDP")); + break; + + case _Jv_SO_SNDBUF_ : + case _Jv_SO_RCVBUF_ : + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, val_len) != 0) + goto error; + return; + case _Jv_SO_REUSEADDR_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val, + val_len) != 0) + goto error; + return; + case _Jv_SO_BINDADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_BINDADDR: read only option")); + return; + case _Jv_IP_MULTICAST_IF_ : + union InAddr u; + jbyteArray haddress; + jbyte *bytes; + int len; + int level, opname; + const char *ptr; + + haddress = ((::java::net::InetAddress *) value)->addr; + bytes = elements (haddress); + len = haddress->length; + if (len == 4) + { + level = IPPROTO_IP; + opname = IP_MULTICAST_IF; + memcpy (&u.addr, bytes, len); + len = sizeof (struct in_addr); + ptr = (const char *) &u.addr; + } +// Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF +#if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF) + else if (len == 16) + { + level = IPPROTO_IPV6; + opname = IPV6_MULTICAST_IF; + memcpy (&u.addr6, bytes, len); + len = sizeof (struct in6_addr); + ptr = (const char *) &u.addr6; + } +#endif + else + throw + new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (::setsockopt (fnum, level, opname, ptr, len) != 0) + goto error; + return; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_LOOP: not yet implemented")); + break; + + case _Jv_IP_TOS_ : + if (::setsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + val_len) != 0) + goto error; + return; + + case _Jv_SO_TIMEOUT_ : + timeout = val; + return; + default : + WSASetLastError (WSAENOPROTOOPT); + } + + error: + _Jv_ThrowSocketException (); +} + +::java::lang::Object * +gnu::java::net::PlainDatagramSocketImpl::getOption (jint optID) +{ + int val; + socklen_t val_len = sizeof(val); + union SockAddr u; + socklen_t addrlen = sizeof(u); + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("TCP_NODELAY not valid for UDP")); + break; + case _Jv_SO_LINGER_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_LINGER not valid for UDP")); + break; + case _Jv_SO_KEEPALIVE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_KEEPALIVE not valid for UDP")); + break; + + case _Jv_SO_BROADCAST_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); + + case _Jv_SO_OOBINLINE_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_OOBINLINE not valid for UDP")); + break; + + case _Jv_SO_RCVBUF_ : + case _Jv_SO_SNDBUF_ : + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, &val_len) != 0) + goto error; + else + return new ::java::lang::Integer (val); + break; + case _Jv_SO_BINDADDR_: + // cache the local address + if (localAddress == NULL) + { + jbyteArray laddr; + if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0) + goto error; + if (u.address.sin_family == AF_INET) + { + laddr = JvNewByteArray (4); + memcpy (elements (laddr), &u.address.sin_addr, 4); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + laddr = JvNewByteArray (16); + memcpy (elements (laddr), &u.address6.sin6_addr, 16); + } +#endif + else + throw new ::java::net::SocketException ( + JvNewStringUTF ("invalid family")); + localAddress = new ::java::net::InetAddress (laddr, NULL); + } + return localAddress; + break; + case _Jv_SO_REUSEADDR_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); + break; + case _Jv_IP_MULTICAST_IF_ : + struct in_addr inaddr; + socklen_t inaddr_len; + char *bytes; + + inaddr_len = sizeof(inaddr); + if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr, + &inaddr_len) != 0) + goto error; + + bytes = inet_ntoa (inaddr); + + return ::java::net::InetAddress::getByName (JvNewStringLatin1 (bytes)); + break; + case _Jv_SO_TIMEOUT_ : + return new ::java::lang::Integer (timeout); + break; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_MULTICAST_LOOP, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean (val != 0); + + case _Jv_IP_TOS_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Integer (val); + + default : + WSASetLastError (WSAENOPROTOOPT); + } + +error: + _Jv_ThrowSocketException (); + return 0; + // we should never get here +} diff --git a/libjava/gnu/java/net/natPlainSocketImplNoNet.cc b/libjava/gnu/java/net/natPlainSocketImplNoNet.cc new file mode 100644 index 0000000..7465d63 --- /dev/null +++ b/libjava/gnu/java/net/natPlainSocketImplNoNet.cc @@ -0,0 +1,128 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> + +#include <gnu/java/net/PlainSocketImpl.h> +#include <java/io/IOException.h> +#include <java/net/BindException.h> +#include <java/net/ConnectException.h> +#include <java/net/SocketException.h> + +void +gnu::java::net::PlainSocketImpl::create (jboolean) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("SocketImpl.create: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::bind (::java::net::InetAddress *, jint) +{ + throw new ::java::net::BindException ( + JvNewStringLatin1 ("SocketImpl.bind: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::connect (::java::net::SocketAddress *, jint) +{ + throw new ::java::net::ConnectException ( + JvNewStringLatin1 ("SocketImpl.connect: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::listen (jint) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("SocketImpl.listen: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::accept (gnu::java::net::PlainSocketImpl *) +{ + throw new ::java::io::IOException ( + JvNewStringLatin1 ("SocketImpl.accept: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::setOption (jint, ::java::lang::Object *) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.setOption: unimplemented")); +} + +::java::lang::Object * +gnu::java::net::PlainSocketImpl::getOption (jint) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.getOption: unimplemented")); +} + +jint +gnu::java::net::PlainSocketImpl::read(void) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.read: unimplemented")); +} + +jint +gnu::java::net::PlainSocketImpl::read(jbyteArray buffer, jint offset, jint count) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.read: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::write(jint b) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.write: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::write(jbyteArray b, jint offset, jint len) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.write: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::sendUrgentData(jint data) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.sendUrgentData: unimplemented")); +} + +jint +gnu::java::net::PlainSocketImpl::available(void) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.available: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::close(void) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.close: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::shutdownInput (void) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.shutdownInput: unimplemented")); +} + +void +gnu::java::net::PlainSocketImpl::shutdownOutput (void) +{ + throw new ::java::net::SocketException ( + JvNewStringLatin1 ("SocketImpl.shutdownOutput: unimplemented")); +} diff --git a/libjava/gnu/java/net/natPlainSocketImplPosix.cc b/libjava/gnu/java/net/natPlainSocketImplPosix.cc new file mode 100644 index 0000000..0b0bb1d --- /dev/null +++ b/libjava/gnu/java/net/natPlainSocketImplPosix.cc @@ -0,0 +1,857 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> + +#ifdef HAVE_SYS_IOCTL_H +#define BSD_COMP /* Get FIONREAD on Solaris2. */ +#include <sys/ioctl.h> +#endif + +// Pick up FIONREAD on Solaris 2.5. +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <errno.h> +#include <string.h> + +#if HAVE_BSTRING_H +// Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 +#include <bstring.h> +#endif + +#include <gcj/cni.h> +#include <gcj/javaprims.h> +#include <gnu/java/net/PlainSocketImpl.h> +#include <java/io/IOException.h> +#include <java/io/InterruptedIOException.h> +#include <java/net/BindException.h> +#include <java/net/ConnectException.h> +#include <java/net/InetAddress.h> +#include <java/net/InetSocketAddress.h> +#include <java/net/SocketException.h> +#include <java/net/SocketTimeoutException.h> +#include <java/lang/InternalError.h> +#include <java/lang/Object.h> +#include <java/lang/Boolean.h> +#include <java/lang/Class.h> +#include <java/lang/Integer.h> +#include <java/lang/Thread.h> +#include <java/lang/NullPointerException.h> +#include <java/lang/ArrayIndexOutOfBoundsException.h> +#include <java/lang/IllegalArgumentException.h> + +union SockAddr +{ + struct sockaddr_in address; +#ifdef HAVE_INET6 + struct sockaddr_in6 address6; +#endif +}; + +void +gnu::java::net::PlainSocketImpl::create (jboolean stream) +{ + int sock = _Jv_socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (sock < 0) + { + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); + } + + _Jv_platform_close_on_exec (sock); + + // We use fnum in place of fd here. From leaving fd null we avoid + // the double close problem in FileDescriptor.finalize. + fnum = sock; +} + +void +gnu::java::net::PlainSocketImpl::bind (::java::net::InetAddress *host, jint lport) +{ + union SockAddr u; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + int i = 1; + + if (len == 4) + { + u.address.sin_family = AF_INET; + + if (host != NULL) + memcpy (&u.address.sin_addr, bytes, len); + else + u.address.sin_addr.s_addr = htonl (INADDR_ANY); + + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (lport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (lport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + // Enable SO_REUSEADDR, so that servers can reuse ports left in TIME_WAIT. + ::setsockopt(fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof(i)); + + if (_Jv_bind (fnum, ptr, len) == 0) + { + address = host; + socklen_t addrlen = sizeof(u); + + if (lport != 0) + localport = lport; + else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0) + localport = ntohs (u.address.sin_port); + else + goto error; + + return; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::BindException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainSocketImpl::connect (::java::net::SocketAddress *addr, + jint timeout) +{ + ::java::net::InetSocketAddress *tmp = (::java::net::InetSocketAddress*) addr; + ::java::net::InetAddress *host = tmp->getAddress(); + jint rport = tmp->getPort(); + + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + if (len == 4) + { + u.address.sin_family = AF_INET; + memcpy (&u.address.sin_addr, bytes, len); + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (rport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (rport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (timeout > 0) + { + int flags = ::fcntl (fnum, F_GETFL); + ::fcntl (fnum, F_SETFL, flags | O_NONBLOCK); + + if ((_Jv_connect (fnum, ptr, len) != 0) && (errno != EINPROGRESS)) + goto error; + + fd_set fset; + struct timeval tv; + FD_ZERO(&fset); + FD_SET(fnum, &fset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + int retval; + + if ((retval = _Jv_select (fnum + 1, &fset, &fset, NULL, &tv)) < 0) + goto error; + else if (retval == 0) + throw new ::java::net::SocketTimeoutException + (JvNewStringUTF ("Connect timed out")); + // Set the socket back into a blocking state. + ::fcntl (fnum, F_SETFL, flags); + } + else + { + if (_Jv_connect (fnum, ptr, len) != 0) + goto error; + } + + address = host; + port = rport; + + // A bind may not have been done on this socket; if so, set localport now. + if (localport == 0) + { + if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0) + localport = ntohs (u.address.sin_port); + else + goto error; + } + + return; + + error: + char* strerr = strerror (errno); + throw new ::java::net::ConnectException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainSocketImpl::listen (jint backlog) +{ + if (::listen (fnum, backlog) != 0) + { + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); + } +} + +void +gnu::java::net::PlainSocketImpl::accept (gnu::java::net::PlainSocketImpl *s) +{ + union SockAddr u; + socklen_t addrlen = sizeof(u); + int new_socket = 0; + + // Do timeouts via select since SO_RCVTIMEO is not always available. + if (timeout > 0 && fnum >= 0 && fnum < FD_SETSIZE) + { + fd_set fset; + struct timeval tv; + FD_ZERO(&fset); + FD_SET(fnum, &fset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + int retval; + if ((retval = _Jv_select (fnum + 1, &fset, &fset, NULL, &tv)) < 0) + goto error; + else if (retval == 0) + throw new ::java::net::SocketTimeoutException ( + JvNewStringUTF("Accept timed out")); + } + + new_socket = _Jv_accept (fnum, (sockaddr*) &u, &addrlen); + + if (new_socket < 0) + goto error; + + _Jv_platform_close_on_exec (new_socket); + + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + s->fnum = new_socket; + s->localport = localport; + s->address = new ::java::net::InetAddress (raddr, NULL); + s->port = rport; + return; + + error: + char* strerr = strerror (errno); + throw new ::java::io::IOException (JvNewStringUTF (strerr)); +} + +// Close(shutdown) the socket. +void +gnu::java::net::PlainSocketImpl::close() +{ + // Avoid races from asynchronous finalization. + JvSynchronize sync (this); + + // should we use shutdown here? how would that effect so_linger? + int res = _Jv_close (fnum); + + if (res == -1) + { + // These three errors are not errors according to tests performed + // on the reference implementation. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new ::java::io::IOException (JvNewStringUTF (strerror (errno))); + } + // Safe place to reset the file pointer. + fnum = -1; + timeout = 0; +} + +// Write a byte to the socket. +void +gnu::java::net::PlainSocketImpl::write(jint b) +{ + jbyte d =(jbyte) b; + int r = 0; + + while (r != 1) + { + r = _Jv_write (fnum, &d, 1); + if (r == -1) + { + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe + = new ::java::io::InterruptedIOException + (JvNewStringLatin1 (strerror (errno))); + iioe->bytesTransferred = 0; + throw iioe; + } + // Some errors should not cause exceptions. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new ::java::io::IOException (JvNewStringUTF (strerror (errno))); + break; + } + } +} + +// Write some bytes to the socket. +void +gnu::java::net::PlainSocketImpl::write(jbyteArray b, jint offset, jint len) +{ + if (! b) + throw new ::java::lang::NullPointerException; + if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b)) + throw new ::java::lang::ArrayIndexOutOfBoundsException; + + jbyte *bytes = elements (b) + offset; + int written = 0; + + while (len > 0) + { + int r = _Jv_write (fnum, bytes, len); + + if (r == -1) + { + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe + = new ::java::io::InterruptedIOException + (JvNewStringLatin1 (strerror (errno))); + iioe->bytesTransferred = written; + throw iioe; + } + // Some errors should not cause exceptions. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new ::java::io::IOException (JvNewStringUTF (strerror (errno))); + break; + } + + written += r; + len -= r; + bytes += r; + } +} + +void +gnu::java::net::PlainSocketImpl::sendUrgentData (jint) +{ + throw new ::java::net::SocketException (JvNewStringLatin1 ( + "PlainSocketImpl: sending of urgent data not supported by this socket")); +} + +// Read a single byte from the socket. +jint +gnu::java::net::PlainSocketImpl::read(void) +{ + jbyte b; + + // Do timeouts via select. + if (timeout > 0 && fnum >= 0 && fnum < FD_SETSIZE) + { + // Create the file descriptor set. + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (fnum,&read_fds); + // Create the timeout struct based on our internal timeout value. + struct timeval timeout_value; + timeout_value.tv_sec = timeout / 1000; + timeout_value.tv_usec = (timeout % 1000) * 1000; + // Select on the fds. + int sel_retval = + _Jv_select (fnum + 1, &read_fds, NULL, NULL, &timeout_value); + // If select returns 0 we've waited without getting data... + // that means we've timed out. + if (sel_retval == 0) + throw new ::java::net::SocketTimeoutException + (JvNewStringUTF ("read timed out") ); + // If select returns ok we know we either got signalled or read some data... + // either way we need to try to read. + } + + int r = _Jv_read (fnum, &b, 1); + + if (r == 0) + return -1; + + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe = + new ::java::io::InterruptedIOException + (JvNewStringUTF("read interrupted")); + iioe->bytesTransferred = r == -1 ? 0 : r; + throw iioe; + } + else if (r == -1) + { + // Some errors cause us to return end of stream... + if (errno == ENOTCONN) + return -1; + + // Other errors need to be signalled. + throw new ::java::io::IOException (JvNewStringUTF (strerror (errno))); + } + + return b & 0xFF; +} + +// Read count bytes into the buffer, starting at offset. +jint +gnu::java::net::PlainSocketImpl::read(jbyteArray buffer, jint offset, jint count) +{ + if (! buffer) + throw new ::java::lang::NullPointerException; + + jsize bsize = JvGetArrayLength (buffer); + + if (offset < 0 || count < 0 || offset + count > bsize) + throw new ::java::lang::ArrayIndexOutOfBoundsException; + + jbyte *bytes = elements (buffer) + offset; + + // Do timeouts via select. + if (timeout > 0 && fnum >= 0 && fnum < FD_SETSIZE) + { + // Create the file descriptor set. + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (fnum, &read_fds); + // Create the timeout struct based on our internal timeout value. + struct timeval timeout_value; + timeout_value.tv_sec = timeout / 1000; + timeout_value.tv_usec =(timeout % 1000) * 1000; + // Select on the fds. + int sel_retval = + _Jv_select (fnum + 1, &read_fds, NULL, NULL, &timeout_value); + // We're only interested in the 0 return. + // error returns still require us to try to read + // the socket to see what happened. + if (sel_retval == 0) + { + ::java::net::SocketTimeoutException *timeoutException = + new ::java::net::SocketTimeoutException + (JvNewStringUTF ("read timed out")); + throw timeoutException; + } + } + + // Read the socket. + int r = ::recv (fnum, (char *) bytes, count, 0); + + if (r == 0) + return -1; + + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe = + new ::java::io::InterruptedIOException + (JvNewStringUTF ("read interrupted")); + iioe->bytesTransferred = r == -1 ? 0 : r; + throw iioe; + } + else if (r == -1) + { + // Some errors cause us to return end of stream... + if (errno == ENOTCONN) + return -1; + + // Other errors need to be signalled. + throw new ::java::io::IOException (JvNewStringUTF (strerror (errno))); + } + + return r; +} + +// How many bytes are available? +jint +gnu::java::net::PlainSocketImpl::available(void) +{ +#if defined(FIONREAD) || defined(HAVE_SELECT) + long num = 0; + int r = 0; + bool num_set = false; + +#if defined(FIONREAD) + r = ::ioctl (fnum, FIONREAD, &num); + + if (r == -1 && errno == ENOTTY) + { + // If the ioctl doesn't work, we don't care. + r = 0; + num = 0; + } + else + num_set = true; +#elif defined(HAVE_SELECT) + if (fnum < 0) + { + errno = EBADF; + r = -1; + } +#endif + + if (r == -1) + { + posix_error: + throw new ::java::io::IOException(JvNewStringUTF(strerror(errno))); + } + + // If we didn't get anything we can use select. + +#if defined(HAVE_SELECT) + if (! num_set) + if (! num_set && fnum >= 0 && fnum < FD_SETSIZE) + { + fd_set rd; + FD_ZERO (&rd); + FD_SET (fnum, &rd); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + r = _Jv_select (fnum + 1, &rd, NULL, NULL, &tv); + if(r == -1) + goto posix_error; + num = r == 0 ? 0 : 1; + } +#endif /* HAVE_SELECT */ + + return (jint) num; +#else + throw new ::java::io::IOException (JvNewStringUTF ("unimplemented")); +#endif +} + +void +gnu::java::net::PlainSocketImpl::setOption (jint optID, ::java::lang::Object *value) +{ + int val; + socklen_t val_len = sizeof (val); + + if (fnum < 0) + throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed")); + + if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$)) + { + ::java::lang::Boolean *boolobj = + static_cast< ::java::lang::Boolean *> (value); + if (boolobj->booleanValue()) + val = 1; + else + { + if (optID == _Jv_SO_LINGER_) + val = -1; + else + val = 0; + } + } + else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$)) + { + ::java::lang::Integer *intobj = + static_cast< ::java::lang::Integer *> (value); + val = (int) intobj->intValue(); + } + else + { + throw new ::java::lang::IllegalArgumentException ( + JvNewStringLatin1 ("`value' must be Boolean or Integer")); + } + + switch (optID) + { + case _Jv_TCP_NODELAY_ : +#ifdef TCP_NODELAY + if (::setsockopt (fnum, IPPROTO_TCP, TCP_NODELAY, (char *) &val, + val_len) != 0) + goto error; +#else + throw new ::java::lang::InternalError + (JvNewStringUTF ("TCP_NODELAY not supported")); +#endif /* TCP_NODELAY */ + return; + + case _Jv_SO_KEEPALIVE_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_KEEPALIVE, (char *) &val, + val_len) != 0) + goto error; + break; + + case _Jv_SO_BROADCAST_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("SO_BROADCAST not valid for TCP")); + break; + + case _Jv_SO_OOBINLINE_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_OOBINLINE, (char *) &val, + val_len) != 0) + goto error; + break; + + case _Jv_SO_LINGER_ : +#ifdef SO_LINGER + struct linger l_val; + l_val.l_onoff = (val != -1); + l_val.l_linger = val; + + if (::setsockopt (fnum, SOL_SOCKET, SO_LINGER, (char *) &l_val, + sizeof(l_val)) != 0) + goto error; +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_LINGER not supported")); +#endif /* SO_LINGER */ + return; + + case _Jv_SO_SNDBUF_ : + case _Jv_SO_RCVBUF_ : +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, val_len) != 0) + goto error; +#else + throw new ::java::lang::InternalError ( + JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported")); +#endif + return; + + case _Jv_SO_BINDADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_BINDADDR: read only option")); + return; + + case _Jv_IP_MULTICAST_IF_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")); + return; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP")); + break; + + case _Jv_IP_TOS_ : + if (::setsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + val_len) != 0) + goto error; + break; + + case _Jv_SO_REUSEADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")); + return; + + case _Jv_SO_TIMEOUT_ : + timeout = val; + return; + + default : + errno = ENOPROTOOPT; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::SocketException (JvNewStringUTF (strerr)); +} + +::java::lang::Object * +gnu::java::net::PlainSocketImpl::getOption (jint optID) +{ + int val; + socklen_t val_len = sizeof(val); + union SockAddr u; + socklen_t addrlen = sizeof(u); + struct linger l_val; + socklen_t l_val_len = sizeof(l_val); + + switch (optID) + { +#ifdef TCP_NODELAY + case _Jv_TCP_NODELAY_ : + if (::getsockopt (fnum, IPPROTO_TCP, TCP_NODELAY, (char *) &val, + &val_len) != 0) + goto error; + else + return new ::java::lang::Boolean (val != 0); +#else + throw new ::java::lang::InternalError + (JvNewStringUTF ("TCP_NODELAY not supported")); +#endif + break; + + case _Jv_SO_LINGER_ : +#ifdef SO_LINGER + if (::getsockopt (fnum, SOL_SOCKET, SO_LINGER, (char *) &l_val, + &l_val_len) != 0) + goto error; + + if (l_val.l_onoff) + return new ::java::lang::Integer (l_val.l_linger); + else + return new ::java::lang::Boolean ((jboolean)false); +#else + throw new ::java::lang::InternalError + (JvNewStringUTF ("SO_LINGER not supported")); +#endif + break; + + case _Jv_SO_KEEPALIVE_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_KEEPALIVE, (char *) &val, + &val_len) != 0) + goto error; + else + return new ::java::lang::Boolean (val != 0); + + case _Jv_SO_BROADCAST_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean ((jboolean)val); + + case _Jv_SO_OOBINLINE_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_OOBINLINE, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Boolean ((jboolean)val); + + case _Jv_SO_RCVBUF_ : + case _Jv_SO_SNDBUF_ : +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, &val_len) != 0) + goto error; + else + return new ::java::lang::Integer (val); +#else + throw new ::java::lang::InternalError + (JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported")); +#endif + break; + case _Jv_SO_BINDADDR_: + // cache the local address + if (localAddress == NULL) + { + jbyteArray laddr; + + if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0) + goto error; + + if (u.address.sin_family == AF_INET) + { + laddr = JvNewByteArray (4); + memcpy (elements (laddr), &u.address.sin_addr, 4); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + laddr = JvNewByteArray (16); + memcpy (elements (laddr), &u.address6.sin6_addr, 16); + } +#endif + else + throw new ::java::net::SocketException + (JvNewStringUTF ("invalid family")); + localAddress = new ::java::net::InetAddress (laddr, NULL); + } + + return localAddress; + break; + case _Jv_IP_MULTICAST_IF_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP")); + break; + + case _Jv_IP_TOS_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + &val_len) != 0) + goto error; + return new ::java::lang::Integer (val); + break; + + case _Jv_SO_REUSEADDR_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")); + break; + + case _Jv_SO_TIMEOUT_ : + return new ::java::lang::Integer (timeout); + break; + + default : + errno = ENOPROTOOPT; + } + + error: + char* strerr = strerror (errno); + throw new ::java::net::SocketException (JvNewStringUTF (strerr)); +} + +void +gnu::java::net::PlainSocketImpl::shutdownInput (void) +{ + if (::shutdown (fnum, 0)) + throw new ::java::net::SocketException (JvNewStringUTF (strerror (errno))); +} + +void +gnu::java::net::PlainSocketImpl::shutdownOutput (void) +{ + if (::shutdown (fnum, 1)) + throw new ::java::net::SocketException (JvNewStringUTF (strerror (errno))); +} diff --git a/libjava/gnu/java/net/natPlainSocketImplWin32.cc b/libjava/gnu/java/net/natPlainSocketImplWin32.cc new file mode 100644 index 0000000..e4837ed --- /dev/null +++ b/libjava/gnu/java/net/natPlainSocketImplWin32.cc @@ -0,0 +1,784 @@ +/* Copyright (C) 2003 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> +#include <platform.h> + +#undef STRICT +#undef MAX_PRIORITY +#undef MIN_PRIORITY + +#include <gnu/java/net/PlainSocketImpl.h> +#include <java/io/IOException.h> +#include <java/io/InterruptedIOException.h> +#include <java/net/BindException.h> +#include <java/net/ConnectException.h> +#include <java/net/InetAddress.h> +#include <java/net/InetSocketAddress.h> +#include <java/net/SocketException.h> +#include <java/net/SocketTimeoutException.h> +#include <java/lang/InternalError.h> +#include <java/lang/Object.h> +#include <java/lang/Boolean.h> +#include <java/lang/Class.h> +#include <java/lang/Integer.h> +#include <java/lang/Thread.h> +#include <java/lang/NullPointerException.h> +#include <java/lang/ArrayIndexOutOfBoundsException.h> +#include <java/lang/IllegalArgumentException.h> + +union SockAddr +{ + struct sockaddr_in address; +#ifdef HAVE_INET6 + struct sockaddr_in6 address6; +#endif +}; + +void +gnu::java::net::PlainSocketImpl::create (jboolean stream) +{ + int sock = ::socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (sock == int(INVALID_SOCKET)) + { + _Jv_ThrowIOException (); + } + + _Jv_platform_close_on_exec (sock); + + // We use fnum in place of fd here. From leaving fd null we avoid + // the double close problem in FileDescriptor.finalize. + fnum = sock; +} + +void +gnu::java::net::PlainSocketImpl::bind (::java::net::InetAddress *host, jint lport) +{ + union SockAddr u; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + int i = 1; + + if (len == 4) + { + u.address.sin_family = AF_INET; + + if (host != NULL) + memcpy (&u.address.sin_addr, bytes, len); + else + u.address.sin_addr.s_addr = htonl (INADDR_ANY); + + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (lport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (lport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + // Enable SO_REUSEADDR, so that servers can reuse ports left in TIME_WAIT. + ::setsockopt(fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof(i)); + + if (::bind (fnum, ptr, len) != SOCKET_ERROR) + { + address = host; + socklen_t addrlen = sizeof(u); + + if (lport != 0) + localport = lport; + else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != SOCKET_ERROR) + localport = ntohs (u.address.sin_port); + else + goto error; + + return; + } + +error: + DWORD dwErrorCode = WSAGetLastError (); + throw new ::java::net::BindException (_Jv_WinStrError (dwErrorCode)); +} + +static void +throwConnectException (DWORD dwErrorCode) +{ + throw new ::java::net::ConnectException (_Jv_WinStrError (dwErrorCode)); +} + +static void +throwConnectException () +{ + throwConnectException (WSAGetLastError ()); +} + +void +gnu::java::net::PlainSocketImpl::connect (::java::net::SocketAddress *addr, + jint timeout) +{ + ::java::net::InetSocketAddress *tmp = (::java::net::InetSocketAddress*) addr; + ::java::net::InetAddress *host = tmp->getAddress(); + jint rport = tmp->getPort(); + + union SockAddr u; + socklen_t addrlen = sizeof(u); + jbyteArray haddress = host->addr; + jbyte *bytes = elements (haddress); + int len = haddress->length; + struct sockaddr *ptr = (struct sockaddr *) &u.address; + + if (len == 4) + { + u.address.sin_family = AF_INET; + memcpy (&u.address.sin_addr, bytes, len); + len = sizeof (struct sockaddr_in); + u.address.sin_port = htons (rport); + } +#ifdef HAVE_INET6 + else if (len == 16) + { + u.address6.sin6_family = AF_INET6; + memcpy (&u.address6.sin6_addr, bytes, len); + len = sizeof (struct sockaddr_in6); + u.address6.sin6_port = htons (rport); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid length")); + + if (timeout > 0) + { + // FIXME: we're creating a fresh WSAEVENT for each connect(). + WSAEventWrapper aWSAEventWrapper(fnum, FD_CONNECT); + WSAEVENT hEvent = aWSAEventWrapper.getEventHandle (); + + if (::connect (fnum, ptr, len) == SOCKET_ERROR) + { + if (WSAGetLastError () != WSAEWOULDBLOCK) + throwConnectException (); + + DWORD dwRet = + WSAWaitForMultipleEvents (1, &hEvent, true, timeout, false); + // use true, false instead of TRUE, FALSE because the + // MS constants got undefined + + if (dwRet == WSA_WAIT_FAILED) + throwConnectException (); + + else if (dwRet == WSA_WAIT_TIMEOUT) + throw new ::java::net::SocketTimeoutException + (JvNewStringUTF ("connect timed out")); + + // If we get here, we still need to check whether the actual + // connect() succeeded. Use any socket-specific error code + // instead of the thread-based one. + int nErrCode; int nErrLen=sizeof(nErrCode); + if (::getsockopt(fnum, SOL_SOCKET, SO_ERROR, (char*) &nErrCode, + &nErrLen) == SOCKET_ERROR) + { + throwConnectException (); + } + + if (nErrCode != NO_ERROR) + { + throwConnectException (nErrCode); + } + } + } + else + { + if (::connect (fnum, ptr, len) == SOCKET_ERROR) + throwConnectException(); + } + + address = host; + port = rport; + + // A bind may not have been done on this socket; if so, set localport now. + if (localport == 0) + { + if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != SOCKET_ERROR) + localport = ntohs (u.address.sin_port); + else + throwConnectException(); + } +} + +void +gnu::java::net::PlainSocketImpl::listen (jint backlog) +{ + if (::listen (fnum, backlog) == SOCKET_ERROR) + { + _Jv_ThrowIOException (); + } +} + +void +gnu::java::net::PlainSocketImpl::accept (::java::net::PlainSocketImpl *s) +{ + union SockAddr u; + socklen_t addrlen = sizeof(u); + int new_socket = 0; + + if (timeout > 0) + { + // FIXME: we're creating a fresh WSAEVENT for each accept(). + // One possible alternative would be that fnum really points + // to an extended structure consisting of the SOCKET, its + // associated WSAEVENT, etc. + WSAEventWrapper aWSAEventWrapper(fnum, FD_ACCEPT); + WSAEVENT hEvent = aWSAEventWrapper.getEventHandle (); + + for (;;) + { + new_socket = ::accept (fnum, (sockaddr*) &u, &addrlen); + + if (new_socket != int(INVALID_SOCKET)) + { + // This new child socket is nonblocking because the parent + // socket became nonblocking via the WSAEventSelect() call, + // so we set its mode back to blocking. + WSAEventSelect (new_socket, hEvent, 0); + // undo the hEvent <-> FD_ACCEPT association inherited + // inherited from our parent socket + + unsigned long lSockOpt = 0L; + // blocking mode + if (ioctlsocket(new_socket, FIONBIO, &lSockOpt) == SOCKET_ERROR) + { + goto error; + } + break; + } + else if (WSAGetLastError () != WSAEWOULDBLOCK) + { + goto error; + } + + DWORD dwRet = + WSAWaitForMultipleEvents (1, &hEvent, true, timeout, false); + // use true, false instead of TRUE, FALSE because the + // MS constants got undefined + + if (dwRet == WSA_WAIT_FAILED) + goto error; + else if (dwRet == WSA_WAIT_TIMEOUT) + throw new ::java::net::SocketTimeoutException + (JvNewStringUTF ("accept timed out")); + } + } + else + { + new_socket = ::accept (fnum, (sockaddr*) &u, &addrlen); + } + + if (new_socket == int(INVALID_SOCKET)) + goto error; + + _Jv_platform_close_on_exec (new_socket); + + jbyteArray raddr; + jint rport; + if (u.address.sin_family == AF_INET) + { + raddr = JvNewByteArray (4); + memcpy (elements (raddr), &u.address.sin_addr, 4); + rport = ntohs (u.address.sin_port); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + raddr = JvNewByteArray (16); + memcpy (elements (raddr), &u.address6.sin6_addr, 16); + rport = ntohs (u.address6.sin6_port); + } +#endif + else + throw new ::java::net::SocketException (JvNewStringUTF ("invalid family")); + + s->fnum = new_socket; + s->localport = localport; + s->address = new InetAddress (raddr, NULL); + s->port = rport; + return; + + error: + _Jv_ThrowIOException (); +} + +// Close(shutdown) the socket. +void +gnu::java::net::PlainSocketImpl::close() +{ + // Avoid races from asynchronous finalization. + JvSynchronize sync (this); + + // should we use shutdown here? how would that effect so_linger? + int res = ::closesocket (fnum); + + if (res == -1) + { + // These three errors are not errors according to tests performed + // on the reference implementation. + DWORD dwErr = WSAGetLastError(); + if (dwErr != WSAENOTCONN && dwErr != WSAECONNRESET + && dwErr != WSAENOTSOCK) + _Jv_ThrowIOException (); + } + // Safe place to reset the file pointer. + fnum = -1; + timeout = 0; +} + +// Write a byte to the socket. +void +gnu::java::net::PlainSocketImpl::write(jint b) +{ + jbyte d =(jbyte) b; + int r = 0; + + while (r != 1) + { + r = ::send (fnum, (char*) &d, 1, 0); + if (r == -1) + { + DWORD dwErr = WSAGetLastError(); + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe + = new ::java::io::InterruptedIOException + (_Jv_WinStrError (dwErr)); + iioe->bytesTransferred = 0; + throw iioe; + } + // Some errors should not cause exceptions. + if (dwErr != WSAENOTCONN && dwErr != WSAECONNRESET + && dwErr != WSAENOTSOCK) + _Jv_ThrowIOException (); + break; + } + } +} + +// Write some bytes to the socket. +void +gnu::java::net::PlainSocketImpl::write(jbyteArray b, jint offset, jint len) +{ + if (! b) + throw new ::java::lang::NullPointerException; + if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b)) + throw new ::java::lang::ArrayIndexOutOfBoundsException; + + jbyte *bytes = elements (b) + offset; + int written = 0; + while (len > 0) + { + int r = ::send (fnum, (char*) bytes, len, 0); + + if (r == -1) + { + DWORD dwErr = WSAGetLastError(); + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe + = new ::java::io::InterruptedIOException + (_Jv_WinStrError (dwErr)); + iioe->bytesTransferred = written; + throw iioe; + } + // Some errors should not cause exceptions. + if (dwErr != WSAENOTCONN && dwErr != WSAECONNRESET + && dwErr != WSAENOTSOCK) + _Jv_ThrowIOException (); + break; + } + + written += r; + len -= r; + bytes += r; + } +} + +void +gnu::java::net::PlainSocketImpl::sendUrgentData (jint) +{ + throw new ::java::net::SocketException (JvNewStringLatin1 ( + "PlainSocketImpl: sending of urgent data not supported by this socket")); +} + +// read() helper +static jint +doRead(int fnum, void* buf, int count, int timeout) +{ + int r = 0; + DWORD dwErrorCode = 0; + // we are forced to declare this here because + // a call to Thread::interrupted() blanks out + // WSAGetLastError(). + + // FIXME: we unconditionally set SO_RCVTIMEO here + // because we can't detect whether someone has + // gone from a non-zero to zero timeout. What we'd + // really need is a member state variable in addition + // to timeout + int nRet= ::setsockopt(fnum, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)); + if (nRet != NO_ERROR) + { + dwErrorCode = WSAGetLastError (); + goto error; + } + + r = ::recv (fnum, (char*) buf, count, 0); + + if (r == 0) + return -1; + + dwErrorCode = WSAGetLastError (); + // save WSAGetLastError() before calling Thread.interrupted() + + if (::java::lang::Thread::interrupted()) + { + ::java::io::InterruptedIOException *iioe = + new ::java::io::InterruptedIOException + (JvNewStringUTF("read interrupted")); + iioe->bytesTransferred = r == -1 ? 0 : r; + throw iioe; + } + else if (r == -1) + { +error: + // Some errors cause us to return end of stream... + if (dwErrorCode == WSAENOTCONN) + return -1; + + // Other errors need to be signalled. + if (dwErrorCode == WSAETIMEDOUT) + throw new ::java::net::SocketTimeoutException + (JvNewStringUTF ("read timed out") ); + else + _Jv_ThrowIOException (dwErrorCode); + } + + return r; +} + +// Read a single byte from the socket. +jint +gnu::java::net::PlainSocketImpl::read(void) +{ + jbyte b; + doRead(fnum, &b, 1, timeout); + return b & 0xFF; +} + +// Read count bytes into the buffer, starting at offset. +jint +gnu::java::net::PlainSocketImpl::read(jbyteArray buffer, jint offset, jint count) +{ + if (! buffer) + throw new ::java::lang::NullPointerException; + + jsize bsize = JvGetArrayLength (buffer); + + if (offset < 0 || count < 0 || offset + count > bsize) + throw new ::java::lang::ArrayIndexOutOfBoundsException; + + jbyte *bytes = elements (buffer) + offset; + + // Read the socket. + return doRead(fnum, bytes, count, timeout); +} + +// How many bytes are available? +jint +gnu::java::net::PlainSocketImpl::available(void) +{ + unsigned long num = 0; + + if (::ioctlsocket (fnum, FIONREAD, &num) == SOCKET_ERROR) + _Jv_ThrowIOException (); + + return (jint) num; +} + +void +gnu::java::net::PlainSocketImpl::setOption (jint optID, ::java::lang::Object *value) +{ + int val; + socklen_t val_len = sizeof (val); + + if (fnum < 0) + throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed")); + + if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$)) + { + ::java::lang::Boolean *boolobj = + static_cast< ::java::lang::Boolean *> (value); + if (boolobj->booleanValue()) + val = 1; + else + { + if (optID == _Jv_SO_LINGER_) + val = -1; + else + val = 0; + } + } + else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$)) + { + ::java::lang::Integer *intobj = + static_cast< ::java::lang::Integer *> (value); + val = (int) intobj->intValue(); + } + else + { + throw new ::java::lang::IllegalArgumentException ( + JvNewStringLatin1 ("`value' must be Boolean or Integer")); + } + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + if (::setsockopt (fnum, IPPROTO_TCP, TCP_NODELAY, (char *) &val, + val_len) == SOCKET_ERROR) + goto error; + return; + + case _Jv_SO_KEEPALIVE_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_KEEPALIVE, (char *) &val, + val_len) == SOCKET_ERROR) + goto error; + break; + + case _Jv_SO_BROADCAST_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("SO_BROADCAST not valid for TCP")); + break; + + case _Jv_SO_OOBINLINE_ : + if (::setsockopt (fnum, SOL_SOCKET, SO_OOBINLINE, (char *) &val, + val_len) == SOCKET_ERROR) + goto error; + break; + + case _Jv_SO_LINGER_ : + struct linger l_val; + l_val.l_onoff = (val != -1); + l_val.l_linger = val; + + if (::setsockopt (fnum, SOL_SOCKET, SO_LINGER, (char *) &l_val, + sizeof(l_val)) == SOCKET_ERROR) + goto error; + return; + + case _Jv_SO_SNDBUF_ : + case _Jv_SO_RCVBUF_ : + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, + val_len) == SOCKET_ERROR) + goto error; + return; + + case _Jv_SO_BINDADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_BINDADDR: read only option")); + return; + + case _Jv_IP_MULTICAST_IF_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")); + return; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP")); + break; + + case _Jv_IP_TOS_ : + if (::setsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + val_len) == SOCKET_ERROR) + goto error; + break; + + case _Jv_SO_REUSEADDR_ : + throw new ::java::net::SocketException ( + JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")); + return; + + case _Jv_SO_TIMEOUT_ : + timeout = val; + return; + + default : + WSASetLastError (WSAENOPROTOOPT); + } + +error: + _Jv_ThrowSocketException (); +} + +::java::lang::Object * +gnu::java::net::PlainSocketImpl::getOption (jint optID) +{ + int val; + socklen_t val_len = sizeof(val); + union SockAddr u; + socklen_t addrlen = sizeof(u); + struct linger l_val; + socklen_t l_val_len = sizeof(l_val); + + switch (optID) + { + case _Jv_TCP_NODELAY_ : + if (::getsockopt (fnum, IPPROTO_TCP, TCP_NODELAY, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + else + return new ::java::lang::Boolean (val != 0); + break; + + case _Jv_SO_LINGER_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_LINGER, (char *) &l_val, + &l_val_len) == SOCKET_ERROR) + goto error; + + if (l_val.l_onoff) + return new ::java::lang::Integer (l_val.l_linger); + else + return new ::java::lang::Boolean ((jboolean)false); + break; + + case _Jv_SO_KEEPALIVE_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_KEEPALIVE, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + else + return new ::java::lang::Boolean (val != 0); + + case _Jv_SO_BROADCAST_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + return new ::java::lang::Boolean ((jboolean)val); + + case _Jv_SO_OOBINLINE_ : + if (::getsockopt (fnum, SOL_SOCKET, SO_OOBINLINE, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + return new ::java::lang::Boolean ((jboolean)val); + + case _Jv_SO_RCVBUF_ : + case _Jv_SO_SNDBUF_ : + int opt; + optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF; + if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + else + return new ::java::lang::Integer (val); + break; + case _Jv_SO_BINDADDR_: + // cache the local address + if (localAddress == NULL) + { + jbyteArray laddr; + + if (::getsockname (fnum, (sockaddr*) &u, + &addrlen) == SOCKET_ERROR) + goto error; + + if (u.address.sin_family == AF_INET) + { + laddr = JvNewByteArray (4); + memcpy (elements (laddr), &u.address.sin_addr, 4); + } +#ifdef HAVE_INET6 + else if (u.address.sin_family == AF_INET6) + { + laddr = JvNewByteArray (16); + memcpy (elements (laddr), &u.address6.sin6_addr, 16); + } +#endif + else + throw new ::java::net::SocketException + (JvNewStringUTF ("invalid family")); + localAddress = new ::java::net::InetAddress (laddr, NULL); + } + + return localAddress; + break; + case _Jv_IP_MULTICAST_IF_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_IF2_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP")); + break; + + case _Jv_IP_MULTICAST_LOOP_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP")); + break; + + case _Jv_IP_TOS_ : + if (::getsockopt (fnum, SOL_SOCKET, IP_TOS, (char *) &val, + &val_len) == SOCKET_ERROR) + goto error; + return new ::java::lang::Integer (val); + break; + + case _Jv_SO_REUSEADDR_ : + throw new ::java::net::SocketException + (JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")); + break; + + case _Jv_SO_TIMEOUT_ : + return new ::java::lang::Integer (timeout); + break; + + default : + WSASetLastError (WSAENOPROTOOPT); + } + +error: + _Jv_ThrowSocketException (); + return 0; + // we should never get here +} + +void +gnu::java::net::PlainSocketImpl::shutdownInput (void) +{ + if (::shutdown (fnum, 0)) + _Jv_ThrowSocketException (); +} + +void +gnu::java::net::PlainSocketImpl::shutdownOutput (void) +{ + if (::shutdown (fnum, 1)) + _Jv_ThrowSocketException (); +} |