DatagramSocket.java (laddr): Removed.
authorWarren Levy <warrenl@cygnus.com>
Fri, 28 May 1999 19:29:53 +0000 (19:29 +0000)
committerWarren Levy <warrenl@gcc.gnu.org>
Fri, 28 May 1999 19:29:53 +0000 (19:29 +0000)
* java/net/DatagramSocket.java (laddr): Removed.
(DatagramSocket): Removed attempts to get or set laddr if null.
(getLocalAddress): Reimplemented per spec.
* java/net/MulticastSocket.java (setTimeToLive): Throw exception
when ttl is 0.
(joinGroup): Throw NullPointerException if any argument is null.
(leaveGroup): ditto.
* java/net/PlainDatagramSocketImpl.java: Updated comments.
* java/net/PlainSocketImpl.java (timeout): Added.
(getInputStream): Added FIXME comment on how to support timeouts
for TCP.
* java/net/ServerSocket.java (ServerSocket): Added FIXME comment.
* java/net/Socket.java: Added FIXME comments to identify
conflicting specs between the JCL and JDK 1.2 documents.
* java/net/natPlainDatagramSocketImpl.cc (bind): Use INADDR_ANY
if host is null.  Get localport value resolved by kernel if bind
lport is 0.
(receive): Implemented support for timeouts in UDP.
(setOption): Implemented based on natPlainSocketImpl version.
(getOption): ditto.
* java/net/natPlainSocketImpl.cc (bind): Get localport value
resolved by kernel if bind lport is 0.
(connect): Get localport value resolved by kernel if bind wasn't
done to set localport.
(accept): Implemented support for timeouts for ServerSocket.
(setOption): Save value for SO_TIMEOUT.
(getOption): Return timeout for SO_TIMEOUT.

From-SVN: r27230

libjava/ChangeLog
libjava/java/net/DatagramSocket.java
libjava/java/net/MulticastSocket.java
libjava/java/net/PlainDatagramSocketImpl.java
libjava/java/net/PlainSocketImpl.java
libjava/java/net/ServerSocket.java
libjava/java/net/Socket.java
libjava/java/net/natPlainDatagramSocketImpl.cc
libjava/java/net/natPlainSocketImpl.cc

index 030da4d11aa51c633d82279490a3f88722375251..d6111392b68395b207e812479c92c1a252f9b128 100644 (file)
@@ -1,3 +1,33 @@
+1999-05-28  Warren Levy  <warrenl@cygnus.com>
+
+       * java/net/DatagramSocket.java (laddr): Removed.
+       (DatagramSocket): Removed attempts to get or set laddr if null.
+       (getLocalAddress): Reimplemented per spec.
+       * java/net/MulticastSocket.java (setTimeToLive): Throw exception
+       when ttl is 0.
+       (joinGroup): Throw NullPointerException if any argument is null.
+       (leaveGroup): ditto.
+       * java/net/PlainDatagramSocketImpl.java: Updated comments.
+       * java/net/PlainSocketImpl.java (timeout): Added.
+       (getInputStream): Added FIXME comment on how to support timeouts
+       for TCP.
+       * java/net/ServerSocket.java (ServerSocket): Added FIXME comment.
+       * java/net/Socket.java: Added FIXME comments to identify
+       conflicting specs between the JCL and JDK 1.2 documents.
+       * java/net/natPlainDatagramSocketImpl.cc (bind): Use INADDR_ANY
+       if host is null.  Get localport value resolved by kernel if bind
+       lport is 0.
+       (receive): Implemented support for timeouts in UDP.
+       (setOption): Implemented based on natPlainSocketImpl version.
+       (getOption): ditto.
+       * java/net/natPlainSocketImpl.cc (bind): Get localport value
+       resolved by kernel if bind lport is 0.
+       (connect): Get localport value resolved by kernel if bind wasn't
+       done to set localport.
+       (accept): Implemented support for timeouts for ServerSocket.
+       (setOption): Save value for SO_TIMEOUT.
+       (getOption): Return timeout for SO_TIMEOUT.
+
 1999-05-26  Bryce McKinlay <bryce@albatross.co.nz>
 
        * java/net/DatagramSocket.java (getSoTimeout): Verify class type.
index 3bfb0327c4926dd08d07ae3a0e48b30ee318f341..96d9555590be646a96d8a0f50d33636c188faf71 100644 (file)
@@ -25,8 +25,6 @@ import java.io.IOException;
 public class DatagramSocket
 {
   DatagramSocketImpl impl;
-  // FIXME: Shouldn't this be determined by getsockname() instead?
-  InetAddress laddr;
 
   public DatagramSocket() throws SocketException
   {
@@ -53,23 +51,12 @@ public class DatagramSocket
     impl = (DatagramSocketImpl) Class.forName("java.net." + propVal +
                                        "DatagramSocketImpl").newInstance();
     impl.create();
-    // TBD: if this is right then the same should be done in Socket().
-    try
-    {
-      if (laddr == null)
-       laddr = InetAddress.getLocalHost();
-    }
-    catch (UnknownHostException e)
-    {
-      throw new BindException(e.getMessage());
-    }
 
     // For multicasting, set the socket to be reused (Stevens pp. 195-6).
     if (this instanceof MulticastSocket)
       impl.setOption(SocketOptions.SO_REUSEADDR, new Boolean(true));
 
     impl.bind(port, laddr);
-    this.laddr = laddr;
   }
 
   public void close()
@@ -79,7 +66,40 @@ public class DatagramSocket
 
   public InetAddress getLocalAddress()
   {
-    return laddr;
+    SecurityManager s = System.getSecurityManager();
+    // FIXME: JCL p. 510 says this should call checkConnect.  But what
+    // string should be used as the hostname?  Maybe this is just a side
+    // effect of calling InetAddress.getLocalHost.
+    //
+    // And is getOption with SO_BINDADDR the right way to get the address?
+    // Doesn't seem to be since this method doesn't throw a SocketException
+    // and SO_BINADDR can throw one.
+    //
+    // Also see RETURNS section in JCL p. 510 about returning any local
+    // addr "if the current execution context is not allowed to connect to
+    // the network interface that is actually bound to this datagram socket."
+    // How is that done?  via InetAddress.getLocalHost?  But that throws
+    // an UnknownHostException and this method doesn't.
+    //
+    // if (s != null)
+    //   s.checkConnect("localhost", -1);
+    try
+      {
+       return (InetAddress)impl.getOption(SocketOptions.SO_BINDADDR);
+      }
+    catch (SocketException ex)
+      {
+      }
+
+    try
+      {
+       return InetAddress.getLocalHost();
+      }
+    catch (UnknownHostException ex)
+      {
+       // FIXME: This should never happen, so how can we avoid this construct?
+       return null;
+      }
   }
 
   public int getLocalPort()
index 03a6e6b175cfc81959c276d54ebc8eea59e0f453..6759e3bc3ca5c5d39518ad17af3068c885c9e9de 100644 (file)
@@ -76,7 +76,7 @@ public class MulticastSocket extends DatagramSocket
   // JDK1.2
   public void setTimeToLive(int ttl) throws IOException
   {
-    if (ttl < 0 || ttl > 255)
+    if (ttl <= 0 || ttl > 255)
       throw new IllegalArgumentException("Invalid ttl: " + ttl);
 
     impl.setTimeToLive(ttl);
@@ -84,6 +84,10 @@ public class MulticastSocket extends DatagramSocket
 
   public void joinGroup(InetAddress mcastaddr) throws IOException
   {
+    // FIXME: We can't currently rely on NullPointerException being
+    // thrown when we invoke a method on a null object.
+    if (mcastaddr == null)
+      throw new NullPointerException("Null address");
     if (! mcastaddr.isMulticastAddress())
       throw new IOException("Not a Multicast address");
 
@@ -96,6 +100,10 @@ public class MulticastSocket extends DatagramSocket
 
   public void leaveGroup(InetAddress mcastaddr) throws IOException
   {
+    // FIXME: We can't currently rely on NullPointerException being
+    // thrown when we invoke a method on a null object.
+    if (mcastaddr == null)
+      throw new NullPointerException("Null address");
     if (! mcastaddr.isMulticastAddress())
       throw new IOException("Not a Multicast address");
 
index 2a063717cc53c8b533f3b69e06c9962bbe556883..90e296f174935ee2dcfcfa9e097a6c803119ae01 100644 (file)
@@ -36,8 +36,11 @@ class PlainDatagramSocketImpl extends DatagramSocketImpl
                    _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF;
 
   int fnum = -1;
-  InetAddress address; // TBD: DatagramSocket.getLocalAddress()?
-  // FIXME: These values are set/read by setOption/getOption.
+
+  // FIXME: Is this necessary?  Could it help w/ DatagramSocket.getLocalAddress?
+  InetAddress address; 
+
+  // These values are set/read by setOption/getOption.
   int timeout = 0;
   InetAddress iface = null;
   int ttl = -1;
index b8e10ad6dc999407824d62ffbf13e313917851ec..17c8071ac39716d3136973adf92ca90774288c1f 100644 (file)
@@ -37,6 +37,9 @@ class PlainSocketImpl extends SocketImpl
 
   int fnum = -1;
 
+  // This value is set/read by setOption/getOption.
+  int timeout = 0;
+
   public native void setOption(int optID, Object value) throws SocketException;
 
   public native Object getOption(int optID) throws SocketException;
@@ -66,6 +69,7 @@ class PlainSocketImpl extends SocketImpl
 
   protected InputStream getInputStream() throws IOException
   {
+    // FIXME: TODO - Implement class SocketInputStream timeouts in read();
     if (in == null)
       in = new FileInputStream (fd);
     return in;
index ae1e1135e357d47a4cd29a3e6bf633e5c99cfda2..96690faad88db1dec044805386c361f77084a569 100644 (file)
@@ -28,6 +28,8 @@ public class ServerSocket
   public ServerSocket (int port)
     throws java.io.IOException
   {
+    // FIXME: JCL p. 1526 says backlog defaults to 50; is 5 to save space
+    // or a typo?
     this(port, 5);
   }
 
index e4ef2d7eb88c35b376f0bbe8a20c15362769e4c8..db46db18e45acf7fb5591104af537a4b4451b59b 100644 (file)
@@ -42,6 +42,9 @@ public class Socket
     if (s != null)
       s.checkConnect(host, port);
     impl.create(true);
+    // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port,
+    // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as
+    // that default.  JDK 1.2 doc infers not to do a bind.
     impl.connect(host, port);
   }
 
@@ -53,6 +56,9 @@ public class Socket
     if (s != null)
       s.checkConnect(address.getHostName(), port);
     impl.create(true);
+    // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port,
+    // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as
+    // that default.  JDK 1.2 doc infers not to do a bind.
     impl.connect(address, port);
   }
 
@@ -64,6 +70,7 @@ public class Socket
     if (s != null)
       s.checkConnect(host, port);
     impl.create(true);
+    // FIXME: JCL p. 1587 says if localAddr is null, use getLocalAddress().
     impl.bind(localAddr, localPort);
     impl.connect(host, port);
   }
@@ -76,6 +83,7 @@ public class Socket
     if (s != null)
       s.checkConnect(address.getHostName(), port);
     impl.create(true);
+    // FIXME: JCL p. 1587 says if localAddr is null, use getLocalAddress().
     impl.bind(localAddr, localPort);
     impl.connect(address, port);
   }
@@ -91,6 +99,9 @@ public class Socket
     SecurityManager s = System.getSecurityManager();
     if (s != null)
       s.checkConnect(host, port);
+    // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port,
+    // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as
+    // that default.  JDK 1.2 doc infers not to do a bind.
     impl.connect(host, port);
   }
 
@@ -105,6 +116,9 @@ public class Socket
     SecurityManager s = System.getSecurityManager();
     if (s != null)
       s.checkConnect(host.getHostName(), port);
+    // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port,
+    // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as
+    // that default.  JDK 1.2 doc infers not to do a bind.
     impl.connect(host, port);
   }
 
index 42abbe135db4d199a2a638b4b73c3302304995a0..74de74fd0dea2a675c429386d463a7c2f8264f4c 100644 (file)
@@ -10,6 +10,8 @@ details.  */
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/select.h>
 #include <netinet/in.h>
 #include <errno.h>
 #include <stdio.h>
@@ -18,12 +20,16 @@ details.  */
 #include <cni.h>
 #include <java/io/IOException.h>
 #include <java/io/FileDescriptor.h>
+#include <java/io/InterruptedIOException.h>
 #include <java/net/BindException.h>
 #include <java/net/SocketException.h>
 #include <java/net/PlainDatagramSocketImpl.h>
 #include <java/net/InetAddress.h>
 #include <java/net/DatagramPacket.h>
+#include <java/lang/InternalError.h>
+#include <java/lang/Object.h>
 #include <java/lang/Boolean.h>
+#include <java/lang/Integer.h>
 
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
@@ -70,14 +76,25 @@ java::net::PlainDatagramSocketImpl::bind (jint lport,
 {
   // FIXME: prob. need to do a setsockopt with SO_BROADCAST to allow multicast.
   union SockAddr u;
-  jbyteArray haddress = host->address;
-  jbyte *bytes = elements (haddress);
-  int len = haddress->length;
   struct sockaddr *ptr = (struct sockaddr *) &u.address;
+  jbyte *bytes = NULL;
+  // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4.
+  int len = 4; // Initialize for INADDR_ANY in case host is NULL.
+
+  if (host != NULL)
+    {
+      jbyteArray haddress = host->address;
+      bytes = elements (haddress);
+      len = haddress->length;
+    }
+
   if (len == 4)
     {
       u.address.sin_family = AF_INET;
-      memcpy (&u.address.sin_addr, bytes, len);
+      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);
     }
@@ -94,8 +111,15 @@ java::net::PlainDatagramSocketImpl::bind (jint lport,
     goto error;
   if (::bind (fnum, ptr, len) == 0)
     {
+      // FIXME: Is address really necessary to set?
       address = host;
-      localport = lport;
+      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:
@@ -190,7 +214,25 @@ java::net::PlainDatagramSocketImpl::receive (java::net::DatagramPacket *p)
   union SockAddr u;
   socklen_t addrlen = sizeof(u);
   jbyte *dbytes = elements (p->getData());
-  ssize_t retlen =
+  ssize_t retlen = 0;
+
+  // Do timeouts via select since SO_RCVTIMEO is not always available.
+  if (timeout > 0)
+    {
+      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 = select (fnum + 1, &rset, NULL, NULL, &tv)) < 0)
+       goto error;
+      else if (retval == 0)
+       JvThrow (new java::io::InterruptedIOException ());
+    }
+
+  retlen =
     ::recvfrom (fnum, (char *) dbytes, p->getLength(), 0, (sockaddr*) &u,
       &addrlen);
   if (retlen < 0)
@@ -288,17 +330,74 @@ void
 java::net::PlainDatagramSocketImpl::setOption (jint optID,
   java::lang::Object *value)
 {
-  if (optID == _Jv_SO_REUSEADDR_)
+  int val;
+  socklen_t val_len = sizeof (val);
+
+  if ( _Jv_IsInstanceOf(value,
+    java::lang::Class::forName(JvNewStringUTF("java.lang.Boolean"))))
     {
-      // FIXME: Is it possible that a Boolean wasn't passed in?
-      const int on = (((java::lang::Boolean *) value)->booleanValue()) ? 1 : 0;
-      if (::setsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
-         sizeof (int)) == 0)
+      java::lang::Boolean *boolobj = 
+        static_cast<java::lang::Boolean *> (value);
+      val = boolobj->booleanValue() ? 1 : 0;
+    }
+  else if ( _Jv_IsInstanceOf(value,
+      java::lang::Class::forName(JvNewStringUTF("java.lang.Integer"))))
+    {
+      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_ :
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("TCP_NODELAY not valid for UDP")));      
+        return;
+      case _Jv_SO_LINGER_ :
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("SO_LINGER not valid for UDP")));      
+        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
+        JvThrow (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
+        JvThrow (new java::lang::InternalError (
+          JvNewStringUTF ("SO_REUSEADDR not supported")));
+#endif 
+       return;
+      case _Jv_SO_BINDADDR_ :
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("SO_BINDADDR: read only option")));
         return;
+      case _Jv_IP_MULTICAST_IF_ :
+       // FIXME: TODO - Implement IP_MULTICAST_IF.
+        JvThrow (new java::lang::InternalError (
+          JvNewStringUTF ("IP_MULTICAST_IF: option not implemented")));
+        return;
+      case _Jv_SO_TIMEOUT_ :
+       timeout = val;
+        return;
+      default :
+        errno = ENOPROTOOPT;
     }
-  else
-    errno = ENOPROTOOPT;
 
+ error:
   char msg[100];
   char* strerr = strerror (errno);
   sprintf (msg, "DatagramSocketImpl.setOption: %.*s", 80, strerr);
@@ -308,17 +407,81 @@ java::net::PlainDatagramSocketImpl::setOption (jint optID,
 java::lang::Object *
 java::net::PlainDatagramSocketImpl::getOption (jint optID)
 {
-  if (optID == _Jv_SO_REUSEADDR_)
+  int val;
+  socklen_t val_len = sizeof(val);
+  union SockAddr u;
+  socklen_t addrlen = sizeof(u);
+
+  switch (optID)
     {
-      int on;
-      socklen_t len;
-      if (::getsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
-         (socklen_t *) &len) == 0)
-        return new java::lang::Boolean (on == 1);
+      case _Jv_TCP_NODELAY_ :
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("TCP_NODELAY not valid for UDP")));      
+        break;
+
+      case _Jv_SO_LINGER_ :
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("SO_LINGER 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
+        JvThrow (new java::lang::InternalError (
+          JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported")));
+#endif    
+       break;
+      case _Jv_SO_BINDADDR_:
+       // FIXME: Should cache the laddr as an optimization.
+       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
+         goto error;
+       return new java::net::InetAddress (laddr, NULL);
+       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
+        JvThrow (new java::lang::InternalError (
+          JvNewStringUTF ("SO_REUSEADDR not supported")));
+#endif 
+       break;
+      case _Jv_IP_MULTICAST_IF_ :
+       // FIXME: TODO - Implement IP_MULTICAST_IF.
+       JvThrow (new java::lang::InternalError (
+         JvNewStringUTF ("IP_MULTICAST_IF: option not implemented")));
+       break;
+      case _Jv_SO_TIMEOUT_ :
+       return new java::lang::Integer (timeout);
+       break;
+      default :
+       errno = ENOPROTOOPT;
     }
-  else
-    errno = ENOPROTOOPT;
 
+ error:
   char msg[100];
   char* strerr = strerror (errno);
   sprintf (msg, "DatagramSocketImpl.getOption: %.*s", 80, strerr);
index feaaa7799822c04a6098bdc017ceaae5d5c1045d..8ad23cbab6917316a57dc8ecebbdb7a71dc84858 100644 (file)
@@ -10,6 +10,8 @@ details.  */
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/select.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <errno.h>
@@ -20,6 +22,7 @@ details.  */
 #include <javaprims.h>
 #include <java/io/IOException.h>
 #include <java/io/FileDescriptor.h>
+#include <java/io/InterruptedIOException.h>
 #include <java/net/BindException.h>
 #include <java/net/ConnectException.h>
 #include <java/net/PlainSocketImpl.h>
@@ -87,7 +90,13 @@ java::net::PlainSocketImpl::bind (java::net::InetAddress *host, jint lport)
   if (::bind (fnum, ptr, len) == 0)
     {
       address = host;
-      localport = lport;
+      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:
@@ -128,9 +137,12 @@ java::net::PlainSocketImpl::connect (java::net::InetAddress *host, jint rport)
     goto error;
   address = host;
   port = rport;
-  if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0)
-    goto error;
-  localport = ntohs (u.address.sin_port);
+  // 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 msg[100];
@@ -156,7 +168,25 @@ java::net::PlainSocketImpl::accept (java::net::PlainSocketImpl *s)
 {
   union SockAddr u;
   socklen_t addrlen = sizeof(u);
-  int new_socket = ::accept (fnum, (sockaddr*) &u, &addrlen);
+  int new_socket = 0; 
+
+  // Do timeouts via select since SO_RCVTIMEO is not always available.
+  if (timeout > 0)
+    {
+      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 = select (fnum + 1, &rset, NULL, NULL, &tv)) < 0)
+       goto error;
+      else if (retval == 0)
+       JvThrow (new java::io::InterruptedIOException ());
+    }
+
+  new_socket = ::accept (fnum, (sockaddr*) &u, &addrlen);
   if (new_socket < 0)
     goto error;
   jbyteArray raddr;
@@ -260,16 +290,15 @@ java::net::PlainSocketImpl::setOption (jint optID, java::lang::Object *value)
           JvNewStringUTF ("SO_BINDADDR: read only option")));
         return;
       case _Jv_IP_MULTICAST_IF_ :
-        JvThrow (new java::lang::InternalError (
+        JvThrow (new java::net::SocketException (
           JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")));
         return;
       case _Jv_SO_REUSEADDR_ :
-        JvThrow (new java::lang::InternalError (
-          JvNewStringUTF ("SO_REUSEADDR: option not implemented")));
+        JvThrow (new java::net::SocketException (
+          JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")));
         return;
       case _Jv_SO_TIMEOUT_ :
-        JvThrow (new java::lang::InternalError (
-          JvNewStringUTF ("SO_TIMEOUT: option not implemented")));
+       timeout = val;
         return;
       default :
         errno = ENOPROTOOPT;
@@ -336,6 +365,7 @@ java::net::PlainSocketImpl::getOption (jint optID)
 #endif    
        break;
       case _Jv_SO_BINDADDR_:
+       // FIXME: Should cache the laddr as an optimization.
        jbyteArray laddr;
        if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0)
          goto error;
@@ -356,16 +386,15 @@ java::net::PlainSocketImpl::getOption (jint optID)
        return new java::net::InetAddress (laddr, NULL);
        break;
       case _Jv_IP_MULTICAST_IF_ :
-       JvThrow (new java::lang::InternalError (
-         JvNewStringUTF ("IP_MULTICAST_IF: option not implemented")));
+       JvThrow (new java::net::SocketException (
+         JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")));
        break;
       case _Jv_SO_REUSEADDR_ :
-       JvThrow (new java::lang::InternalError (
-         JvNewStringUTF ("SO_REUSEADDR: option not implemented")));
+       JvThrow (new java::net::SocketException (
+         JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")));
        break;
       case _Jv_SO_TIMEOUT_ :
-       JvThrow (new java::lang::InternalError (
-         JvNewStringUTF ("SO_TIMEOUT: option not implemented")));
+       return new java::lang::Integer (timeout);
        break;
       default :
        errno = ENOPROTOOPT;