aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/net/natPlainSocketImpl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/net/natPlainSocketImpl.cc')
-rw-r--r--libjava/java/net/natPlainSocketImpl.cc280
1 files changed, 266 insertions, 14 deletions
diff --git a/libjava/java/net/natPlainSocketImpl.cc b/libjava/java/net/natPlainSocketImpl.cc
index 076ee711948..2e7f9d5c558 100644
--- a/libjava/java/net/natPlainSocketImpl.cc
+++ b/libjava/java/net/natPlainSocketImpl.cc
@@ -10,16 +10,34 @@ 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>
#include <stdio.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 <cni.h>
+#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>
#include <java/net/InetAddress.h>
+#include <java/net/SocketException.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>
#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
@@ -52,14 +70,25 @@ void
java::net::PlainSocketImpl::bind (java::net::InetAddress *host, jint lport)
{
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);
}
@@ -77,20 +106,27 @@ 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:
char msg[100];
char* strerr = strerror (errno);
sprintf (msg, "SocketImpl.bind: %.*s", 80, strerr);
- JvThrow (new java::io::IOException (JvNewStringUTF (msg)));
+ JvThrow (new java::net::BindException (JvNewStringUTF (msg)));
}
void
java::net::PlainSocketImpl::connect (java::net::InetAddress *host, jint rport)
{
union SockAddr u;
+ socklen_t addrlen = sizeof(u);
jbyteArray haddress = host->address;
jbyte *bytes = elements (haddress);
int len = haddress->length;
@@ -113,17 +149,22 @@ java::net::PlainSocketImpl::connect (java::net::InetAddress *host, jint rport)
#endif
else
goto error;
- if (::connect (fnum, ptr, len) == 0)
- {
- address = host;
- port = rport;
- return;
- }
+ if (::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 msg[100];
char* strerr = strerror (errno);
sprintf (msg, "SocketImpl.connect: %.*s", 80, strerr);
- JvThrow (new java::io::IOException (JvNewStringUTF (msg)));
+ JvThrow (new java::net::ConnectException (JvNewStringUTF (msg)));
}
void
@@ -143,7 +184,26 @@ 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 (
+ JvNewStringUTF("Accept timed out")));
+ }
+
+ new_socket = ::accept (fnum, (sockaddr*) &u, &addrlen);
if (new_socket < 0)
goto error;
jbyteArray raddr;
@@ -155,6 +215,7 @@ java::net::PlainSocketImpl::accept (java::net::PlainSocketImpl *s)
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);
@@ -175,3 +236,194 @@ java::net::PlainSocketImpl::accept (java::net::PlainSocketImpl *s)
sprintf (msg, "SocketImpl.accept: %.*s", 80, strerr);
JvThrow (new java::io::IOException (JvNewStringUTF (msg)));
}
+
+void
+java::net::PlainSocketImpl::setOption (jint optID, java::lang::Object *value)
+{
+ int val;
+ socklen_t val_len = sizeof (val);
+
+ if ( _Jv_IsInstanceOf(value,
+ java::lang::Class::forName(JvNewStringUTF("java.lang.Boolean"))))
+ {
+ 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 // assume value is an Integer
+ {
+ java::lang::Integer *intobj =
+ static_cast<java::lang::Integer *> (value);
+ val = (int) intobj->intValue();
+ }
+
+ switch (optID)
+ {
+ case _Jv_TCP_NODELAY_ :
+#ifdef TCP_NODELAY
+ if (::setsockopt (fnum, IPPROTO_TCP, TCP_NODELAY, (char *) &val,
+ val_len) != 0)
+ goto error;
+#else
+ JvThrow (new java::lang::InternalError (
+ JvNewStringUTF ("TCP_NODELAY not supported")));
+#endif /* TCP_NODELAY */
+ return;
+ 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
+ JvThrow (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
+ JvThrow (new java::lang::InternalError (
+ JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF 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_ :
+ JvThrow (new java::net::SocketException (
+ JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")));
+ return;
+ case _Jv_SO_REUSEADDR_ :
+ JvThrow (new java::net::SocketException (
+ JvNewStringUTF ("SO_REUSEADDR: not valid for TCP")));
+ return;
+ case _Jv_SO_TIMEOUT_ :
+ timeout = val;
+ return;
+ default :
+ errno = ENOPROTOOPT;
+ }
+
+ error:
+ char msg[100];
+ char* strerr = strerror (errno);
+ sprintf (msg, "SocketImpl.setOption: %.*s", 80, strerr);
+ JvThrow (new java::net::SocketException (JvNewStringUTF (msg)));
+}
+
+java::lang::Object *
+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
+ JvThrow (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 (false);
+#else
+ JvThrow (new java::lang::InternalError (
+ JvNewStringUTF ("SO_LINGER not supported")));
+#endif
+ 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_:
+ // 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
+ goto error;
+ localAddress = new java::net::InetAddress (laddr, NULL);
+ }
+ return localAddress;
+ break;
+ case _Jv_IP_MULTICAST_IF_ :
+ JvThrow (new java::net::SocketException (
+ JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP")));
+ break;
+ case _Jv_SO_REUSEADDR_ :
+ JvThrow (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 msg[100];
+ char* strerr = strerror (errno);
+ sprintf (msg, "SocketImpl.getOption: %.*s", 80, strerr);
+ JvThrow (new java::net::SocketException (JvNewStringUTF (msg)));
+}