summaryrefslogtreecommitdiff
path: root/native/jni/native-lib/cpnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'native/jni/native-lib/cpnet.c')
-rw-r--r--native/jni/native-lib/cpnet.c718
1 files changed, 718 insertions, 0 deletions
diff --git a/native/jni/native-lib/cpnet.c b/native/jni/native-lib/cpnet.c
new file mode 100644
index 000000000..c112ac2a7
--- /dev/null
+++ b/native/jni/native-lib/cpnet.c
@@ -0,0 +1,718 @@
+/* cpnet.c -
+ Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+#include "config.h"
+#include <jni.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "cpnet.h"
+
+#define SOCKET_DEFAULT_TIMEOUT -1 /* milliseconds */
+
+#if defined (HAVE_MSG_NOSIGNAL)
+#define SOCKET_NOSIGNAL MSG_NOSIGNAL
+#elif defined (HAVE_SO_NOSIGPIPE)
+#define SOCKET_NOSIGNAL SO_NOSIGPIPE
+#else
+#error "No suitable flag found to ommit a SIGPIPE on signal errors with send()."
+#endif
+
+static int socketTimeouts[FD_SETSIZE];
+
+static jint waitForWritable(jint fd)
+{
+ struct timeval tv;
+ fd_set writeset;
+ int ret;
+
+
+ FD_ZERO(&writeset);
+ FD_SET(fd, &writeset);
+ if (socketTimeouts[fd] > 0)
+ {
+ tv.tv_sec = socketTimeouts[fd] / 1000;
+ tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000;
+ ret = select(fd+1, NULL, &writeset, NULL, &tv);
+ }
+ else
+ ret = select(fd+1, NULL, &writeset, NULL, NULL);
+
+ return (ret <= 0) ? -1 : 0;
+}
+
+static jint waitForReadable(jint fd)
+{
+ struct timeval tv;
+ fd_set readset;
+ int ret;
+
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ if (socketTimeouts[fd] > 0)
+ {
+ tv.tv_sec = socketTimeouts[fd] / 1000;
+ tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000;
+ ret = select(fd+1, &readset, NULL, NULL, &tv);
+ }
+ else
+ ret = select(fd+1, &readset, NULL, NULL, NULL);
+
+ return (ret <= 0) ? -1 : 0;
+}
+
+jint cpnet_openSocketStream(JNIEnv *env UNUSED, jint *fd, jint family)
+{
+ *fd = socket(family, SOCK_STREAM, 0);
+ if (*fd == -1)
+ return errno;
+
+ fcntl(*fd, F_SETFD, FD_CLOEXEC);
+ assert(*fd < FD_SETSIZE);
+ socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT;
+ return 0;
+}
+
+jint cpnet_openSocketDatagram(JNIEnv *env UNUSED, jint *fd, jint family)
+{
+ *fd = socket(family, SOCK_DGRAM, 0);
+ if (*fd == -1)
+ return errno;
+
+ fcntl(*fd, F_SETFD, FD_CLOEXEC);
+ assert(*fd < FD_SETSIZE);
+ socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT;
+ return 0;
+}
+
+jint cpnet_shutdown (JNIEnv *env UNUSED, jint fd, jbyte flag)
+{
+ int ret;
+ int shut_flag = 0;
+
+ if (flag == CPNET_SHUTDOWN_READ)
+ shut_flag = SHUT_RD;
+ else if (flag == CPNET_SHUTDOWN_WRITE)
+ shut_flag = SHUT_WR;
+
+ ret = shutdown (fd, shut_flag);
+ if (ret != 0)
+ return errno;
+ return 0;
+}
+
+jint cpnet_close(JNIEnv *env UNUSED, jint fd)
+{
+ if (close (fd) != 0)
+ return errno;
+ return 0;
+}
+
+jint cpnet_listen(JNIEnv *env UNUSED, jint fd, jint queuelen)
+{
+ if (listen (fd, queuelen) != 0)
+ return errno;
+ return 0;
+}
+
+jint cpnet_accept(JNIEnv *env UNUSED, jint fd, jint *newfd)
+{
+ if (waitForReadable (fd) < 0)
+ return ETIMEDOUT;
+
+ *newfd = accept(fd, NULL, 0);
+ if (*newfd != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_bind(JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
+{
+ int ret;
+
+ ret = bind(fd, (struct sockaddr *)addr->data, addr->len);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_connect(JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
+{
+ int ret;
+
+ /* TODO: implement socket time out */
+ ret = connect(fd, (struct sockaddr *)addr->data, addr->len);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getLocalAddr(JNIEnv *env, jint fd, cpnet_address **addr)
+{
+ socklen_t slen = 1024;
+ int ret;
+
+ *addr = JCL_malloc(env, slen);
+
+ slen -= sizeof(jint);
+ ret = getsockname(fd, (struct sockaddr *)(*addr)->data, &slen );
+ if (ret != 0)
+ {
+ int err = errno;
+ JCL_free(env, *addr);
+ return err;
+ }
+
+ (*addr)->len = slen;
+
+ return 0;
+}
+
+jint cpnet_getRemoteAddr(JNIEnv *env, jint fd, cpnet_address **addr)
+{
+ socklen_t slen = 1024;
+ int ret;
+
+ *addr = JCL_malloc(env, slen);
+
+ slen -= sizeof(jint);
+ ret = getpeername(fd, (struct sockaddr *)(*addr)->data, &slen );
+ if (ret != 0)
+ {
+ int err = errno;
+ JCL_free(env, *addr);
+ return err;
+ }
+
+ (*addr)->len = slen;
+
+ return 0;
+}
+
+jint cpnet_setBroadcast(JNIEnv *env UNUSED, jint fd, jint flag)
+{
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_send (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_sent)
+{
+ ssize_t ret;
+
+ if (waitForWritable(fd) < 0)
+ return ETIMEDOUT;
+
+ ret = send(fd, data, len, SOCKET_NOSIGNAL);
+ if (ret < 0)
+ return errno;
+
+ *bytes_sent = ret;
+
+ return 0;
+}
+
+jint cpnet_sendTo (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, cpnet_address *addr, jint *bytes_sent)
+{
+ ssize_t ret;
+
+ if (waitForWritable(fd) < 0)
+ return ETIMEDOUT;
+
+ ret = sendto(fd, data, len, SOCKET_NOSIGNAL, (struct sockaddr *)addr->data,
+ addr->len);
+ if (ret < 0)
+ return errno;
+
+ *bytes_sent = ret;
+ return 0;
+}
+
+jint cpnet_recv (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_recv)
+{
+ ssize_t ret;
+
+ if (waitForReadable(fd) < 0)
+ return ETIMEDOUT;
+
+ ret = recv(fd, data, len, 0);
+ if (ret < 0)
+ return errno;
+
+ *bytes_recv = ret;
+
+ return 0;
+}
+
+jint cpnet_recvFrom (JNIEnv *env, jint fd, jbyte *data, jint len, cpnet_address **addr, jint *bytes_recv)
+{
+ socklen_t slen = 1024;
+ ssize_t ret;
+
+ if (waitForReadable(fd) < 0)
+ return ETIMEDOUT;
+
+ *addr = JCL_malloc(env, slen);
+
+ slen -= sizeof(jint);
+ ret = recvfrom(fd, data, len, 0, (struct sockaddr *) (*addr)->data, &slen);
+ if (ret < 0)
+ {
+ int err = errno;
+ JCL_free(env, *addr);
+ return err;
+ }
+
+ (*addr)->len = slen;
+ *bytes_recv = ret;
+
+ return 0;
+}
+
+jint cpnet_setSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint nodelay)
+{
+ socklen_t len = sizeof(jint);
+ int ret;
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, len);
+ if (ret < 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint *nodelay)
+{
+ socklen_t len = sizeof(jint);
+ int ret;
+
+ ret = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, nodelay, &len);
+ if (ret < 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setLinger (JNIEnv *env UNUSED, jint fd, jint flag, jint value)
+{
+ socklen_t len = sizeof(struct linger);
+ int ret;
+ struct linger __linger;
+
+ if (flag)
+ {
+ __linger.l_onoff = 0;
+ }
+ else
+ {
+ __linger.l_linger = value;
+ __linger.l_onoff = 1;
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &__linger, len);
+ if (ret < 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getLinger (JNIEnv *env UNUSED, jint fd, jint *flag, jint *value)
+{
+ socklen_t slen = sizeof(struct linger);
+ struct linger __linger;
+ int ret;
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_LINGER, &__linger, &slen);
+ if (ret != 0)
+ return errno;
+
+ *flag = __linger.l_onoff;
+ *value = __linger.l_linger;
+
+ return ret;
+}
+
+jint cpnet_setSocketTimeout (JNIEnv *env UNUSED, jint fd, jint value)
+{
+ socketTimeouts[fd] = value;
+ return 0;
+}
+
+jint cpnet_getSocketTimeout (JNIEnv *env UNUSED, jint fd, jint *value)
+{
+ *value = socketTimeouts[fd];
+ return 0;
+}
+
+jint cpnet_setSendBuf (JNIEnv *env UNUSED, jint fd, jint value)
+{
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getSendBuf (JNIEnv *env UNUSED, jint fd, jint *value)
+{
+ int ret;
+ socklen_t slen = sizeof(*value);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, value, &slen);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setRecvBuf (JNIEnv *env UNUSED, jint fd, jint value)
+{
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getRecvBuf (JNIEnv *env UNUSED, jint fd, jint *value)
+{
+ int ret;
+ socklen_t slen = sizeof(*value);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, value, &slen);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setTTL (JNIEnv *env UNUSED, jint fd, jint value)
+{
+ int ret;
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_TTL, &value, sizeof(value));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getTTL (JNIEnv *env UNUSED, jint fd, jint *value)
+{
+ int ret;
+ socklen_t slen = sizeof(*value);
+
+ ret = getsockopt(fd, IPPROTO_IP, IP_TTL, value, &slen);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setMulticastIF (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
+{
+ int ret;
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (struct sockaddr *)addr->data, addr->len);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getMulticastIF (JNIEnv *env, jint fd, cpnet_address **addr)
+{
+ socklen_t slen = 1024;
+ int ret;
+
+ *addr = JCL_malloc(env, slen);
+
+ slen -= sizeof(jint);
+ ret = getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (struct sockaddr *)(*addr)->data, &slen);
+ (*addr)->len = slen;
+
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setReuseAddress (JNIEnv *env UNUSED, jint fd, jint reuse)
+{
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getReuseAddress (JNIEnv *env UNUSED, jint fd, jint *reuse)
+{
+ int ret;
+ socklen_t slen = sizeof(*reuse);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reuse, &slen);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_setKeepAlive (JNIEnv *env UNUSED, jint fd, jint keep)
+{
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep, sizeof(keep));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getKeepAlive (JNIEnv *env UNUSED, jint fd, jint *keep)
+{
+ int ret;
+ socklen_t slen = sizeof(*keep);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, keep, &slen);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_addMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
+{
+ struct ip_mreq req;
+ int ret;
+
+ memset(&req, 0, sizeof(req));
+ req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr;
+ req.imr_interface.s_addr = INADDR_ANY;
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_dropMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
+{
+ struct ip_mreq req;
+ int ret;
+
+ memset(&req, 0, sizeof(req));
+ req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr;
+ req.imr_interface.s_addr = INADDR_ANY;
+ ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &req, sizeof(req));
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getAvailableBytes (JNIEnv *env UNUSED, jint fd, jint *availableBytes)
+{
+ int ret;
+
+ ret = ioctl(fd, FIONREAD, availableBytes);
+ if (ret != 0)
+ return errno;
+
+ return 0;
+}
+
+jint cpnet_getHostname (JNIEnv *env UNUSED, char *hostname, jint hostname_len)
+{
+ int ret;
+
+ ret = gethostname(hostname, hostname_len);
+ if (ret != 0)
+ return errno;
+
+ hostname[hostname_len-1] = 0;
+ return 0;
+}
+
+jint cpnet_getHostByName (JNIEnv *env, const char *hostname, cpnet_address ***addresses, jint *addresses_count)
+{
+ struct hostent hret;
+ struct hostent *result;
+ jint buflen = 1024;
+ int herr;
+ int ret;
+ int counter = 0;
+ cpnet_address **addr_arr;
+ int i;
+ char *buf;
+
+ do
+ {
+ buf = (char *)JCL_malloc(env, buflen);
+#ifdef HAVE_GETHOSTBYNAME_R
+ ret = gethostbyname_r (hostname, &hret, buf, buflen, &result, &herr);
+#else
+ ret = gethostbyname (hostname);
+#endif
+ if (ret != 0 || result == NULL)
+ {
+ if (herr == ERANGE)
+ {
+ buflen *= 2;
+ JCL_free(env, buf);
+ continue;
+ }
+ JCL_free(env, buf);
+
+ return -herr;
+ }
+
+ break;
+ }
+ while (1);
+
+ while (hret.h_addr_list[counter] != NULL)
+ counter++;
+
+ *addresses_count = counter;
+ addr_arr = *addresses = JCL_malloc(env, sizeof(cpnet_address *) * counter);
+ switch (hret.h_addrtype)
+ {
+ case AF_INET:
+ for (i = 0; i < counter; i++)
+ {
+ addr_arr[i] = cpnet_newIPV4Address(env);
+ cpnet_bytesToIPV4Address(addr_arr[i], (jbyte *)hret.h_addr_list[i]);
+ }
+ break;
+ case AF_INET6:
+ for (i = 0; i < counter; i++)
+ {
+ addr_arr[i] = cpnet_newIPV6Address(env);
+ cpnet_bytesToIPV6Address(addr_arr[i], (jbyte *)hret.h_addr_list[i]);
+ }
+ break;
+ default:
+ *addresses_count = 0;
+ JCL_free(env, addr_arr);
+ break;
+ }
+
+ JCL_free(env, buf);
+
+ return 0;
+}
+
+jint cpnet_getHostByAddr (JNIEnv *env UNUSED, cpnet_address *addr, char *hostname, jint hostname_len)
+{
+ union
+ {
+ struct sockaddr_in *addr_v4;
+ struct sockaddr_in6 *addr_v6;
+ char *data;
+ } haddr;
+ void *raw_addr;
+ int addr_type;
+ struct hostent *ret;
+ int addr_len;
+
+ haddr.data = addr->data;
+
+ if (haddr.addr_v4->sin_family == AF_INET)
+ {
+ raw_addr = &haddr.addr_v4->sin_addr;
+ addr_len = sizeof(haddr.addr_v4->sin_addr);
+ addr_type = AF_INET;
+ }
+ else if (haddr.addr_v6->sin6_family == AF_INET6)
+ {
+ raw_addr = &haddr.addr_v6->sin6_addr;
+ addr_type = AF_INET6;
+ addr_len = sizeof(haddr.addr_v6->sin6_addr);
+ }
+ else
+ return EINVAL;
+
+ /* Here we do not have any thread safe call. VM implementors will have to
+ * do a big lock. Or it should be put on the Classpath VM interface.
+ */
+ ret = gethostbyaddr(raw_addr, addr_len, addr_type);
+ if (ret == NULL)
+ {
+ /* The trouble here is how to distinguish the two cases ? */
+ if (h_errno != 0)
+ return h_errno;
+ else
+ return errno;
+
+ }
+ strncpy(hostname, ret->h_name, hostname_len);
+
+ return 0;
+}
+
+void cpnet_freeAddresses(JNIEnv * env, cpnet_address **addr, jint addresses_count)
+{
+ jint i;
+
+ for (i = 0; i < addresses_count; i++)
+ cpnet_freeAddress(env, addr[i]);
+}