| /* Copyright (C) 2003, 2004, 2005 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 <gnu/java/net/PlainSocketImpl$SocketInputStream.h> |
| #include <gnu/java/net/PlainSocketImpl$SocketOutputStream.h> |
| #include <java/io/IOException.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) |
| { |
| SOCKET sock = ::socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0); |
| |
| if (sock == INVALID_SOCKET) |
| { |
| _Jv_ThrowIOException (); |
| } |
| |
| // Cast this to a HANDLE so we can make |
| // it non-inheritable via _Jv_platform_close_on_exec. |
| HANDLE hSocket = (HANDLE) sock; |
| _Jv_platform_close_on_exec (hSocket); |
| |
| // We use native_fd in place of fd here. From leaving fd null we avoid |
| // the double close problem in FileDescriptor.finalize. |
| native_fd = (jint) hSocket; |
| } |
| |
| 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; |
| |
| 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 (native_fd, ptr, len) != SOCKET_ERROR) |
| { |
| socklen_t addrlen = sizeof(u); |
| |
| if (lport != 0) |
| localport = lport; |
| else if (::getsockname (native_fd, (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(); |
| |
| // Set the SocketImpl's address and port fields before we try to |
| // connect. Note that the fact that these are set doesn't imply |
| // that we're actually connected to anything. We need to record |
| // this data before we attempt the connect, since non-blocking |
| // SocketChannels will use this and almost certainly throw timeout |
| // exceptions. |
| address = host; |
| port = rport; |
| |
| 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(native_fd, FD_CONNECT); |
| WSAEVENT hEvent = aWSAEventWrapper.getEventHandle (); |
| |
| if (::connect (native_fd, 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 |
| |
| // Reset and ignore our thread's interrupted flag. |
| // It's not possible to interrupt these sort of |
| // operations on Win32 anyway. |
| ::java::lang::Thread::interrupted(); |
| |
| 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(native_fd, SOL_SOCKET, SO_ERROR, (char*) &nErrCode, |
| &nErrLen) == SOCKET_ERROR) |
| { |
| throwConnectException (); |
| } |
| |
| if (nErrCode != NO_ERROR) |
| { |
| throwConnectException (nErrCode); |
| } |
| } |
| } |
| else |
| { |
| if (::connect (native_fd, ptr, len) == SOCKET_ERROR) |
| throwConnectException(); |
| } |
| |
| // A bind may not have been done on this socket; if so, set localport now. |
| if (localport == 0) |
| { |
| if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != SOCKET_ERROR) |
| localport = ntohs (u.address.sin_port); |
| else |
| throwConnectException(); |
| } |
| } |
| |
| void |
| gnu::java::net::PlainSocketImpl::listen (jint backlog) |
| { |
| if (::listen (native_fd, backlog) == SOCKET_ERROR) |
| { |
| _Jv_ThrowIOException (); |
| } |
| } |
| |
| void |
| gnu::java::net::PlainSocketImpl::accept (gnu::java::net::PlainSocketImpl *s) |
| { |
| union SockAddr u; |
| socklen_t addrlen = sizeof(u); |
| HANDLE hSocket = 0; |
| SOCKET new_socket = 0; |
| |
| if (timeout > 0) |
| { |
| // FIXME: we're creating a fresh WSAEVENT for each accept(). |
| // One possible alternative would be that native_fd really points |
| // to an extended structure consisting of the SOCKET, its |
| // associated WSAEVENT, etc. |
| WSAEventWrapper aWSAEventWrapper(native_fd, FD_ACCEPT); |
| WSAEVENT hEvent = aWSAEventWrapper.getEventHandle (); |
| |
| for (;;) |
| { |
| new_socket = ::accept (native_fd, (sockaddr*) &u, &addrlen); |
| |
| if (new_socket != 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 |
| |
| // Reset and ignore our thread's interrupted flag. |
| ::java::lang::Thread::interrupted(); |
| |
| 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 (native_fd, (sockaddr*) &u, &addrlen); |
| } |
| |
| if (new_socket == INVALID_SOCKET) |
| goto error; |
| |
| // Cast this to a HANDLE so we can make |
| // it non-inheritable via _Jv_platform_close_on_exec. |
| hSocket = (HANDLE) new_socket; |
| _Jv_platform_close_on_exec (hSocket); |
| |
| 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->native_fd = (jint) hSocket; |
| s->localport = localport; |
| s->address = new ::java::net::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 (native_fd); |
| |
| 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. |
| native_fd = -1; |
| timeout = 0; |
| } |
| |
| // Write a byte to the socket. |
| void |
| gnu::java::net::PlainSocketImpl$SocketOutputStream::write(jint b) |
| { |
| jbyte d =(jbyte) b; |
| int r = 0; |
| |
| while (r != 1) |
| { |
| r = ::send (this$0->native_fd, (char*) &d, 1, 0); |
| if (r == -1) |
| { |
| DWORD dwErr = WSAGetLastError(); |
| |
| // Reset and ignore our thread's interrupted flag. |
| // It's not possible to interrupt these sort of |
| // operations on Win32 anyway. |
| ::java::lang::Thread::interrupted(); |
| |
| // 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$SocketOutputStream::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 (this$0->native_fd, (char*) bytes, len, 0); |
| |
| if (r == -1) |
| { |
| DWORD dwErr = WSAGetLastError(); |
| |
| // Reset and ignore our thread's interrupted flag. |
| ::java::lang::Thread::interrupted(); |
| |
| // 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 native_fd, 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(native_fd, SOL_SOCKET, SO_RCVTIMEO, |
| (char*)&timeout, sizeof(timeout)); |
| if (nRet != NO_ERROR) |
| { |
| dwErrorCode = WSAGetLastError (); |
| goto error; |
| } |
| |
| r = ::recv (native_fd, (char*) buf, count, 0); |
| |
| if (r == 0) |
| return -1; |
| |
| dwErrorCode = WSAGetLastError (); |
| // save WSAGetLastError() before calling Thread.interrupted() |
| |
| // Reset and ignore our thread's interrupted flag. |
| ::java::lang::Thread::interrupted(); |
| |
| 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$SocketInputStream::read(void) |
| { |
| jbyte b; |
| doRead(this$0->native_fd, &b, 1, this$0->timeout); |
| return b & 0xFF; |
| } |
| |
| // Read count bytes into the buffer, starting at offset. |
| jint |
| gnu::java::net::PlainSocketImpl$SocketInputStream::read(jbyteArray buffer, |
| jint offset, jint count) |
| { |
| // If zero bytes were requested, short circuit so that recv |
| // doesn't signal EOF. |
| if (count == 0) |
| return 0; |
| |
| 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(this$0->native_fd, bytes, count, this$0->timeout); |
| } |
| |
| // How many bytes are available? |
| jint |
| gnu::java::net::PlainSocketImpl::available(void) |
| { |
| unsigned long num = 0; |
| |
| if (::ioctlsocket (native_fd, 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 (native_fd < 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 (native_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val, |
| val_len) == SOCKET_ERROR) |
| goto error; |
| return; |
| |
| case _Jv_SO_KEEPALIVE_ : |
| if (::setsockopt (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, 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 (native_fd, (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 (native_fd, 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 (native_fd, 0)) |
| _Jv_ThrowSocketException (); |
| } |
| |
| void |
| gnu::java::net::PlainSocketImpl::shutdownOutput (void) |
| { |
| if (::shutdown (native_fd, 1)) |
| _Jv_ThrowSocketException (); |
| } |