summaryrefslogtreecommitdiff
path: root/native/jni/java-net/javanet.c
diff options
context:
space:
mode:
Diffstat (limited to 'native/jni/java-net/javanet.c')
-rw-r--r--native/jni/java-net/javanet.c1049
1 files changed, 1049 insertions, 0 deletions
diff --git a/native/jni/java-net/javanet.c b/native/jni/java-net/javanet.c
new file mode 100644
index 000000000..49261edf9
--- /dev/null
+++ b/native/jni/java-net/javanet.c
@@ -0,0 +1,1049 @@
+/* javanet.c - Common internal functions for the java.net package
+ Copyright (C) 1998 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.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <jni.h>
+#include <jcl.h>
+
+#include "javanet.h"
+
+/* Need to have some value for SO_TIMEOUT */
+#ifndef SO_TIMEOUT
+#ifndef SO_RCVTIMEO
+#warning Neither SO_TIMEOUT or SO_RCVTIMEO are defined!
+#warning This will cause all get/setOption calls with that value to throw an exception
+#else
+#define SO_TIMEOUT SO_RCVTIMEO
+#endif /* not SO_RCVTIMEO */
+#endif /* not SO_TIMEOUT */
+
+/*************************************************************************/
+
+/*
+ * Sets an integer field in the specified object.
+ */
+static void
+_javanet_set_int_field(JNIEnv *env, jobject obj, char *class, char *field,
+ int val)
+{
+ jclass cls;
+ jfieldID fid;
+
+ cls = (*env)->FindClass(env, class);
+ fid = (*env)->GetFieldID(env, cls, field, "I");
+ if (!fid)
+ return;
+
+ (*env)->SetIntField(env, obj, fid, val);
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Returns the value of the specified integer instance variable field or
+ * -1 if an error occurs.
+ */
+int
+_javanet_get_int_field(JNIEnv *env, jobject obj, const char *field)
+{
+ jclass cls = 0;
+ jfieldID fid;
+ int fd;
+
+ DBG("Entered _javanet_get_int_field\n");
+
+ cls = (*env)->GetObjectClass(env, obj);
+ fid = (*env)->GetFieldID(env, cls, field, "I");
+ if (!fid)
+ return(-1);
+ DBG("Found field id\n");
+
+ fd = (*env)->GetIntField(env, obj, fid);
+
+ return(fd);
+}
+
+/*************************************************************************/
+
+/*
+ * Creates a FileDescriptor object in the parent class. It is not used
+ * by this implementation, but the docs list it as a variable, so we
+ * need to include it.
+ */
+static void
+_javanet_create_localfd(JNIEnv *env, jobject this)
+{
+ jclass this_cls, fd_cls;
+ jfieldID fid;
+ jmethodID mid;
+ jobject fd_obj;
+
+ DBG("Entered _javanet_create_localfd\n");
+
+ /* Look up the fd field */
+ this_cls = (*env)->FindClass(env, "java/net/SocketImpl");
+ fid = (*env)->GetFieldID(env, this_cls, "fd", "Ljava/io/FileDescriptor;");
+ if (!fid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+ DBG("Found fd variable\n");
+
+ /* Create a FileDescriptor */
+ fd_cls = (*env)->FindClass(env, "java/io/FileDescriptor");
+ if (!fd_cls)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Can't load FileDescriptor class");
+ return;
+ }
+ DBG("Found FileDescriptor class\n");
+
+ mid = (*env)->GetMethodID(env, fd_cls, "<init>", "()V");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+ DBG("Found FileDescriptor constructor\n");
+
+ fd_obj = (*env)->NewObject(env, fd_cls, mid);
+ if (!fd_obj)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+ DBG("Created FileDescriptor\n");
+
+ /* Now set the pointer to the new FileDescriptor */
+ (*env)->SetObjectField(env, this, fid, fd_obj);
+ DBG("Set fd field\n");
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Returns a Boolean object with the specfied value
+ */
+static jobject
+_javanet_create_boolean(JNIEnv *env, jboolean val)
+{
+ jclass cls;
+ jmethodID mid;
+ jobject obj;
+
+ cls = (*env)->FindClass(env, "java/lang/Boolean");
+ if (!cls)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ mid = (*env)->GetMethodID(env, cls, "<init>", "(Z)V");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ obj = (*env)->NewObject(env, cls, mid, val);
+ if (!obj)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ return(obj);
+}
+
+/*************************************************************************/
+
+/*
+ * Returns an Integer object with the specfied value
+ */
+static jobject
+_javanet_create_integer(JNIEnv *env, jint val)
+{
+ jclass cls;
+ jmethodID mid;
+ jobject obj;
+
+ cls = (*env)->FindClass(env, "java/lang/Integer");
+ if (!cls)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ mid = (*env)->GetMethodID(env, cls, "<init>", "(I)V");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ obj = (*env)->NewObject(env, cls, mid, val);
+ if (!obj)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ return(obj);
+}
+
+/*************************************************************************/
+
+/*
+ * Builds an InetAddress object from a 32 bit address in host byte order
+ */
+static jobject
+_javanet_create_inetaddress(JNIEnv *env, int netaddr)
+{
+ char buf[16];
+ jclass ia_cls;
+ jmethodID mid;
+ jstring ip_str;
+ jobject ia;
+
+ /* Build a string IP address */
+ sprintf(buf, "%d.%d.%d.%d", ((netaddr & 0xFF000000) >> 24),
+ ((netaddr & 0x00FF0000) >> 16), ((netaddr &0x0000FF00) >> 8),
+ (netaddr & 0x000000FF));
+ DBG("Created ip addr string\n");
+
+ /* Get an InetAddress object for this IP */
+ ia_cls = (*env)->FindClass(env, "java/net/InetAddress");
+ if (!ia_cls)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Can't load InetAddress class");
+ return(0);
+ }
+ DBG("Found InetAddress class\n");
+
+ mid = (*env)->GetStaticMethodID(env, ia_cls, "getByName",
+ "(Ljava/lang/String;)Ljava/net/InetAddress;");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+ DBG("Found getByName method\n");
+
+ ip_str = (*env)->NewStringUTF(env, buf);
+ if (!ip_str)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+
+ ia = (*env)->CallStaticObjectMethod(env, ia_cls, mid, ip_str);
+ if (!ia)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return(0); }
+ DBG("Called getByName method\n");
+
+ return(ia);
+}
+
+/*************************************************************************/
+
+/*
+ * Set's the value of the "addr" field in PlainSocketImpl with a new
+ * InetAddress for the specified addr
+ */
+static void
+_javanet_set_remhost(JNIEnv *env, jobject this, int netaddr)
+{
+ jclass this_cls;
+ jfieldID fid;
+ jobject ia;
+
+ DBG("Entered _javanet_set_remhost\n");
+
+ /* Get an InetAddress object */
+ ia = _javanet_create_inetaddress(env, netaddr);
+ if (!ia)
+ return;
+
+ /* Set the variable in the object */
+ this_cls = (*env)->FindClass(env, "java/net/SocketImpl");
+ fid = (*env)->GetFieldID(env, this_cls, "address", "Ljava/net/InetAddress;");
+ if (!fid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+ DBG("Found address field\n");
+
+ (*env)->SetObjectField(env, this, fid, ia);
+ DBG("Set field\n");
+}
+
+/*************************************************************************/
+
+/*
+ * Returns a 32 bit Internet address for the passed in InetAddress object
+ */
+int
+_javanet_get_netaddr(JNIEnv *env, jobject addr)
+{
+ jclass cls = 0;
+ jmethodID mid;
+ jarray arr = 0;
+ jbyte *octets;
+ int netaddr, len;
+
+ DBG("Entered _javanet_get_netaddr\n");
+
+ /* Call the getAddress method on the object to retrieve the IP address */
+ cls = (*env)->GetObjectClass(env, addr);
+ mid = (*env)->GetMethodID(env, cls, "getAddress", "()[B");
+ if (!mid)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Internal Error");
+ return(0);
+ }
+ DBG("Got getAddress method\n");
+
+ arr = (*env)->CallObjectMethod(env, addr, mid);
+ if (!arr)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Internal Error");
+ return(0);
+ }
+ DBG("Got the address\n");
+
+ /* Turn the IP address into a 32 bit Internet address in network byte order */
+ len = (*env)->GetArrayLength(env, arr);
+ if (len != 4)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Internal Error");
+ return(0);
+ }
+ DBG("Length ok\n");
+
+ octets = (*env)->GetByteArrayElements(env, arr, 0);
+ if (!octets)
+ {
+ JCL_ThrowException(env, IO_EXCEPTION, "Internal Error");
+ return(0);
+ }
+ DBG("Grabbed bytes\n");
+
+ netaddr = (((unsigned char)octets[0]) << 24) +
+ (((unsigned char)octets[1]) << 16) +
+ (((unsigned char)octets[2]) << 8) +
+ ((unsigned char)octets[3]);
+
+ netaddr = htonl(netaddr);
+
+ (*env)->ReleaseByteArrayElements(env, arr, octets, 0);
+ DBG("Done getting addr\n");
+
+ return(netaddr);
+}
+
+/*************************************************************************/
+
+/*
+ * Creates a new stream or datagram socket
+ */
+void
+_javanet_create(JNIEnv *env, jobject this, jboolean stream)
+{
+ int fd;
+
+ if (stream)
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ else
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return; }
+
+ if (stream)
+ _javanet_set_int_field(env, this, "java/net/PlainSocketImpl",
+ "native_fd", fd);
+ else
+ _javanet_set_int_field(env, this, "java/net/PlainDatagramSocketImpl",
+ "native_fd", fd);
+}
+
+/*************************************************************************/
+
+/*
+ * Close the socket. Any underlying streams will be closed by this
+ * action as well.
+ */
+void
+_javanet_close(JNIEnv *env, jobject this, int stream)
+{
+ int fd = -1;
+
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ return;
+
+ close(fd);
+
+ if (stream)
+ _javanet_set_int_field(env, this, "java/net/PlainSocketImpl",
+ "native_fd", -1);
+ else
+ _javanet_set_int_field(env, this, "java/net/PlainDatagramSocketImpl",
+ "native_fd", -1);
+}
+
+/*************************************************************************/
+
+/*
+ * Connects to the specified destination.
+ */
+void
+_javanet_connect(JNIEnv *env, jobject this, jobject addr, jint port)
+{
+ int netaddr, fd = -1, rc, addrlen;
+ struct sockaddr_in si;
+
+ DBG("Entered _javanet_connect\n");
+
+ /* Pre-process input variables */
+ netaddr = _javanet_get_netaddr(env, addr);
+ if ((*env)->ExceptionOccurred(env))
+ return;
+
+ if (port == -1)
+ port = 0;
+ DBG("Got network address\n");
+
+ /* Grab the real socket file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Socket not yet created"); return; }
+ DBG("Got native fd\n");
+
+ /* Connect up */
+ memset(&si, 0, sizeof(struct sockaddr_in));
+ si.sin_family = AF_INET;
+ si.sin_addr.s_addr = netaddr;
+ si.sin_port = htons(((short)port));
+
+ rc = connect(fd, (struct sockaddr *) &si, sizeof(struct sockaddr_in));
+ if (rc == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return; }
+ DBG("Connected successfully\n");
+
+ /* Populate instance variables */
+ addrlen = sizeof(struct sockaddr_in);
+ rc = getsockname(fd, (struct sockaddr *) &si, &addrlen);
+ if (rc == -1)
+ {
+ close(fd);
+ JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
+ return;
+ }
+
+ _javanet_create_localfd(env, this);
+ if ((*env)->ExceptionOccurred(env))
+ return;
+ DBG("Created fd\n");
+
+ _javanet_set_int_field(env, this, "java/net/SocketImpl", "localport",
+ ntohs(si.sin_port));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+ DBG("Set the local port\n");
+
+ addrlen = sizeof(struct sockaddr_in);
+ rc = getpeername(fd, (struct sockaddr *) &si, &addrlen);
+ if (rc == -1)
+ {
+ close(fd);
+ JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
+ return;
+ }
+
+ _javanet_set_remhost(env, this, ntohl(si.sin_addr.s_addr));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+ DBG("Set the remote host\n");
+
+ _javanet_set_int_field(env, this, "java/net/SocketImpl", "port",
+ ntohs(si.sin_port));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+ DBG("Set the remote port\n");
+}
+
+/*************************************************************************/
+
+/*
+ * This method binds the specified address to the specified local port.
+ * Note that we have to set the local address and local
+ * port public instance variables.
+ */
+void
+_javanet_bind(JNIEnv *env, jobject this, jobject addr, jint port, int stream)
+{
+ jclass cls;
+ jmethodID mid;
+ jbyteArray arr = 0;
+ jbyte *octets;
+ jint fd;
+ struct sockaddr_in si;
+ int namelen;
+
+ DBG("Entering native bind()\n");
+
+ /* Get the address to connect to */
+ cls = (*env)->GetObjectClass(env, addr);
+ mid = (*env)->GetMethodID(env, cls, "getAddress", "()[B");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal error") ; return; }
+ DBG("Past getAddress method id\n");
+
+ arr = (*env)->CallObjectMethod(env, addr, mid);
+ if (!arr || (*env)->ExceptionOccurred(env))
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal error") ; return; }
+ DBG("Past call object method\n");
+
+ octets = (*env)->GetByteArrayElements(env, arr, 0);
+ if (!octets)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal error") ; return; }
+ DBG("Past grab array\n");
+
+ /* Get the native socket file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ {
+ (*env)->ReleaseByteArrayElements(env, arr, octets, 0);
+ JCL_ThrowException(env, IO_EXCEPTION, "Internal error");
+ return;
+ }
+ DBG("Past native_fd lookup\n");
+
+ /* Bind the socket */
+ memset(&si, 0, sizeof(struct sockaddr_in));
+
+ si.sin_family = AF_INET;
+ si.sin_addr.s_addr = *(int *)octets; /* Already in network byte order */
+ if (port == -1)
+ si.sin_port = 0;
+ else
+ si.sin_port = htons(port);
+
+ (*env)->ReleaseByteArrayElements(env, arr, octets, 0);
+
+ if (bind(fd, (struct sockaddr *) &si, sizeof(struct sockaddr_in)) == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return; }
+ DBG("Past bind\n");
+
+ /* Update instance variables, specifically the local port number */
+ namelen = sizeof(struct sockaddr_in);
+ getsockname(fd, (struct sockaddr *) &si, &namelen);
+
+ if (stream)
+ _javanet_set_int_field(env, this, "java/net/SocketImpl",
+ "localport", ntohs(si.sin_port));
+ else
+ _javanet_set_int_field(env, this, "java/net/DatagramSocketImpl",
+ "localPort", ntohs(si.sin_port));
+ DBG("Past update port number\n");
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Starts listening on a socket with the specified number of pending
+ * connections allowed.
+ */
+void
+_javanet_listen(JNIEnv *env, jobject this, jint queuelen)
+{
+ int fd = -1, rc;
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ /* Start listening */
+ rc = listen(fd, queuelen);
+ if (rc == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return; }
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Accepts a new connection and assigns it to the passed in SocketImpl
+ * object. Note that we assume this is a PlainSocketImpl just like us
+ */
+void
+_javanet_accept(JNIEnv *env, jobject this, jobject impl)
+{
+ int fd = -1, newfd, addrlen, rc;
+ struct sockaddr_in si;
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+
+ /* Accept the connection */
+ addrlen = sizeof(struct sockaddr_in);
+ memset(&si, 0, addrlen);
+
+ /******* Do we need to look for EINTR? */
+ newfd = accept(fd, (struct sockaddr *) &si, &addrlen);
+ if (newfd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+
+ /* Populate instance variables */
+ _javanet_set_int_field(env, impl, "java/net/PlainSocketImpl", "native_fd",
+ newfd);
+
+ rc = getsockname(newfd, (struct sockaddr *) &si, &addrlen);
+ if (rc == -1)
+ {
+ close(newfd);
+ JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
+ return;
+ }
+
+ _javanet_create_localfd(env, impl);
+ if ((*env)->ExceptionOccurred(env))
+ return;
+
+ _javanet_set_int_field(env, impl, "java/net/SocketImpl", "localport",
+ ntohs(si.sin_port));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+
+ addrlen = sizeof(struct sockaddr_in);
+ rc = getpeername(newfd, (struct sockaddr *) &si, &addrlen);
+ if (rc == -1)
+ {
+ close(newfd);
+ JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
+ return;
+ }
+
+ _javanet_set_remhost(env, impl, ntohl(si.sin_addr.s_addr));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+
+ _javanet_set_int_field(env, impl, "java/net/SocketImpl", "port",
+ ntohs(si.sin_port));
+ if ((*env)->ExceptionOccurred(env))
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Receives a buffer from a remote host. The args are:
+ *
+ * buf - The byte array into which the data received will be written
+ * offset - Offset into the byte array to start writing
+ * len - The number of bytes to read.
+ * addr - Pointer to 32 bit net address of host to receive from. If null,
+ * this parm is ignored. If pointing to an address of 0, the
+ * actual address read is stored here
+ * port - Pointer to the port to receive from. If null, this parm is ignored.
+ * If it is 0, the actual remote port received from is stored here
+ *
+ * The actual number of bytes read is returned.
+ */
+int
+_javanet_recvfrom(JNIEnv *env, jobject this, jarray buf, int offset, int len,
+ int *addr, int *port)
+{
+ int fd, rc, si_len;
+ jbyte *p;
+ struct sockaddr_in si;
+
+ DBG("Entered _javanet_recvfrom\n");
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, "No Socket"); return 0; }
+ DBG("Got native_fd\n");
+
+ /* Get a pointer to the buffer */
+ p = (*env)->GetByteArrayElements(env, buf, 0);
+ if (!p)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return 0; }
+ DBG("Got buffer\n");
+
+ /* Read the data */
+ for (;;)
+ {
+ if (!addr)
+ rc = recvfrom(fd, p + offset, len, 0, 0, 0);
+ else
+ {
+ memset(&si, 0, sizeof(struct sockaddr_in));
+ si_len = sizeof(struct sockaddr_in);
+ rc = recvfrom(fd, p + offset, len, 0, (struct sockaddr *) &si, &si_len);
+ }
+
+ if ((rc == -1) && (errno == EINTR))
+ continue;
+
+ break;
+ }
+
+ (*env)->ReleaseByteArrayElements(env, buf, p, 0);
+
+ if (rc == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return 0; }
+
+ /* Handle return addr case */
+ if (addr)
+ {
+ *addr = si.sin_addr.s_addr;
+ if (port)
+ *port = si.sin_port;
+ }
+
+ return(rc);
+}
+
+/*************************************************************************/
+
+/*
+ * Sends a buffer to a remote host. The args are:
+ *
+ * buf - A byte array
+ * offset - Index into the byte array to start sendign
+ * len - The number of bytes to write
+ * addr - The 32bit address to send to (may be 0)
+ * port - The port number to send to (may be 0)
+ */
+void
+_javanet_sendto(JNIEnv *env, jobject this, jarray buf, int offset, int len,
+ int addr, int port)
+{
+ int fd, rc;
+ jbyte *p;
+ struct sockaddr_in si;
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, "No Socket"); return; }
+
+ /* Get a pointer to the buffer */
+ p = (*env)->GetByteArrayElements(env, buf, 0);
+ if (!p)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+
+ /* Send the data */
+ if (!addr)
+ rc = send(fd, p + offset, len, 0);
+ else
+ {
+ memset(&si, 0, sizeof(struct sockaddr_in));
+ si.sin_family = AF_INET;
+ si.sin_addr.s_addr = addr;
+ si.sin_port = (unsigned short)port;
+
+ DBG("Sending....\n");
+ rc = sendto(fd, p + offset, len, 0, (struct sockaddr *) &si, sizeof(struct sockaddr_in));
+ }
+
+ (*env)->ReleaseByteArrayElements(env, buf, p, 0);
+
+ /***** Do we need to check EINTR? */
+ if (rc == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); return; }
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Sets the specified option for a socket
+ */
+void
+_javanet_set_option(JNIEnv *env, jobject this, jint option_id, jobject val)
+{
+ int fd = -1, rc;
+ int optval, sockopt;
+ jclass cls;
+ jmethodID mid;
+ struct linger linger;
+ struct sockaddr_in si;
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, IO_EXCEPTION, "Internal Error"); return; }
+
+ /* We need a class object for all cases below */
+ cls = (*env)->GetObjectClass(env, val);
+
+ /* Process the option request */
+ switch (option_id)
+ {
+ /* TCP_NODELAY case. val is a Boolean that tells us what to do */
+ case SOCKOPT_TCP_NODELAY:
+ mid = (*env)->GetMethodID(env, cls, "booleanValue", "()Z");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ /* Should be a 0 or a 1 */
+ optval = (*env)->CallBooleanMethod(env, val, mid);
+
+ rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
+ break;
+
+ /* SO_LINGER case. If val is a boolean, then it will always be set
+ to false indicating disable linger, otherwise it will be an
+ integer that contains the linger value */
+ case SOCKOPT_SO_LINGER:
+ memset(&linger, 0, sizeof(struct linger));
+
+ mid = (*env)->GetMethodID(env, cls, "booleanValue", "()Z");
+ if (mid)
+ {
+ /* We are disabling linger */
+ linger.l_onoff = 0;
+ }
+ else
+ {
+ /* Clear exception if thrown for failure to do method lookup
+ above */
+ if ((*env)->ExceptionOccurred(env))
+ (*env)->ExceptionClear(env);
+
+ mid = (*env)->GetMethodID(env, cls, "intValue", "()I");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ linger.l_linger = (*env)->CallIntMethod(env, val, mid);
+ linger.l_onoff = 1;
+ }
+ rc = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger,
+ sizeof(struct linger));
+ break;
+
+ /* SO_TIMEOUT case. Val will be an integer with the new value */
+ case SOCKOPT_SO_TIMEOUT:
+#ifdef SO_TIMEOUT
+ mid = (*env)->GetMethodID(env, cls, "intValue", "()I");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ optval = (*env)->CallIntMethod(env, val, mid);
+
+ rc = setsockopt(fd, SOL_SOCKET, SO_TIMEOUT, &optval, sizeof(int));
+#else
+ JCL_ThrowException(env, SOCKET_EXCEPTION,
+ "SO_TIMEOUT not supported on this platform");
+ return;
+#endif
+ break;
+
+ case SOCKOPT_SO_SNDBUF:
+ case SOCKOPT_SO_RCVBUF:
+ mid = (*env)->GetMethodID(env, cls, "intValue", "()I");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ optval = (*env)->CallIntMethod(env, val, mid);
+
+ if (option_id == SOCKOPT_SO_SNDBUF)
+ sockopt = SO_SNDBUF;
+ else
+ sockopt = SO_RCVBUF;
+
+ rc = setsockopt(fd, SOL_SOCKET, sockopt, &optval, sizeof(int));
+ break;
+
+ /* TTL case. Val with be an Integer with the new time to live value */
+ case SOCKOPT_IP_TTL:
+ mid = (*env)->GetMethodID(env, cls, "intValue", "()I");
+ if (!mid)
+ { JCL_ThrowException(env, IO_EXCEPTION,
+ "Internal Error"); return; }
+
+ optval = (*env)->CallIntMethod(env, val, mid);
+
+ rc = setsockopt(fd, IPPROTO_IP, IP_TTL, &optval, sizeof(int));
+ break;
+
+ /* Multicast Interface case - val is InetAddress object */
+ case SOCKOPT_IP_MULTICAST_IF:
+ memset(&si, 0, sizeof(struct sockaddr_in));
+ si.sin_family = AF_INET;
+ si.sin_addr.s_addr = _javanet_get_netaddr(env, val);
+
+ if ((*env)->ExceptionOccurred(env))
+ return;
+
+ rc = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &si,
+ sizeof(struct sockaddr_in));
+ break;
+
+ default:
+ JCL_ThrowException(env, SOCKET_EXCEPTION, "Unrecognized option");
+ return;
+ }
+
+ /* Check to see if above operations succeeded */
+ if (rc == -1)
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+
+ return;
+}
+
+/*************************************************************************/
+
+/*
+ * Retrieves the specified option values for a socket
+ */
+jobject
+_javanet_get_option(JNIEnv *env, jobject this, jint option_id)
+{
+ int fd = -1, rc;
+ int optval, optlen, sockopt;
+ struct linger linger;
+ struct sockaddr_in si;
+
+ /* Get the real file descriptor */
+ fd = _javanet_get_int_field(env, this, "native_fd");
+ if (fd == -1)
+ { JCL_ThrowException(env, SOCKET_EXCEPTION, "Internal Error"); return(0); }
+
+ /* Process the option requested */
+ switch (option_id)
+ {
+ /* TCP_NODELAY case. Return a Boolean indicating on or off */
+ case SOCKOPT_TCP_NODELAY:
+ optlen = sizeof(optval);
+ rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, &optlen);
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ if (optval)
+ return(_javanet_create_boolean(env, 1));
+ else
+ return(_javanet_create_boolean(env, 0));
+
+ break;
+
+ /* SO_LINGER case. If disabled, return a Boolean object that represents
+ false, else return an Integer that is the value of SO_LINGER */
+ case SOCKOPT_SO_LINGER:
+ memset(&linger, 0, sizeof(struct linger));
+ optlen = sizeof(struct linger);
+
+ rc = getsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, &optlen);
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ if (linger.l_onoff)
+ return(_javanet_create_integer(env, linger.l_linger));
+ else
+ return(_javanet_create_boolean(env, 0));
+
+ break;
+
+ /* SO_TIMEOUT case. Return an Integer object with the timeout value */
+ case SOCKOPT_SO_TIMEOUT:
+#ifdef SO_TIMEOUT
+ optlen = sizeof(int);
+
+ rc = getsockopt(fd, SOL_SOCKET, SO_TIMEOUT, &optval, &optlen);
+#else
+ JCL_ThrowException(env, SOCKET_EXCEPTION,
+ "SO_TIMEOUT not supported on this platform");
+ return(0);
+#endif /* not SO_TIMEOUT */
+
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ return(_javanet_create_integer(env, optval));
+ break;
+
+ case SOCKOPT_SO_SNDBUF:
+ case SOCKOPT_SO_RCVBUF:
+ optlen = sizeof(int);
+ if (option_id == SOCKOPT_SO_SNDBUF)
+ sockopt = SO_SNDBUF;
+ else
+ sockopt = SO_RCVBUF;
+
+ rc = getsockopt(fd, SOL_SOCKET, sockopt, &optval, &optlen);
+
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ return(_javanet_create_integer(env, optval));
+ break;
+
+ /* The TTL case. Return an Integer with the Time to Live value */
+ case SOCKOPT_IP_TTL:
+ optlen = sizeof(int);
+
+ rc = getsockopt(fd, IPPROTO_IP, IP_TTL, &optval, &optlen);
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ return(_javanet_create_integer(env, optval));
+ break;
+
+ /* Multicast interface case */
+ case SOCKOPT_IP_MULTICAST_IF:
+ memset(&si, 0, sizeof(struct sockaddr_in));
+ optlen = sizeof(struct sockaddr_in);
+
+ rc = getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &si, &optlen);
+ if (rc == -1)
+ {
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ return(_javanet_create_inetaddress(env, ntohl(si.sin_addr.s_addr)));
+ break;
+
+ default:
+ JCL_ThrowException(env, SOCKET_EXCEPTION, strerror(errno));
+ return(0);
+ }
+
+ return(0);
+}
+