/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    setSocketOptionInt
	 * Signature: (Ljava/io/FileDescriptor;II)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_setSocketOptionInt
	(JNIEnv * env, jclass clazz, jobject fd, jint optID, jint value) {
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		
		if(optID == SO_SNDTIMEO || optID == SO_RCVTIMEO) {
			struct timeval optVal;
			optVal.tv_sec = value / 1000;
			optVal.tv_usec = (value % 1000) * 1000;
			int ret = setsockopt(handle, SOL_SOCKET, optID, &optVal, sizeof(optVal));
			if(ret == -1) {
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			}
			return;
		} else if(optID == SO_LINGER) {
			struct linger optVal;
			
			optVal.l_onoff = value >= 0;
			optVal.l_linger = value >= 0 ? value : 0;

			int ret = setsockopt(handle, SOL_SOCKET, optID, &optVal, sizeof(optVal));
			if(ret == -1) {
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			}
			return;
		}
		
		
		int optVal = (int)value;
		
		int ret = setsockopt(handle, SOL_SOCKET, optID, &optVal, sizeof(optVal));
		if(ret == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
		}
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    connect
	 * Signature: (Ljava/lang/String;Ljava/io/FileDescriptor;)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_connect
	(JNIEnv * env, jclass clazz, jstring file, jobject fd, jboolean abstract, jboolean dgram) {
		const char* socketFile = (*env)->GetStringUTFChars(env, file, NULL);
		if(socketFile == NULL) {
			return; // OOME
		}
		if(strlen(socketFile) >= 104) {
			(*env)->ReleaseStringUTFChars(env, file, socketFile);
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Pathname too long for socket", file);
			return;
		}

		int sockType = SOCK_STREAM;

		if ( dgram == JNI_TRUE ) {
			sockType = SOCK_DGRAM;
		}

		int socketHandle = socket(AF_UNIX, sockType, 0);
		if(socketHandle == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}


		struct sockaddr_un su;
		memset(&su, 0, sizeof(struct sockaddr_un));
		su.sun_family = AF_UNIX;
		#ifdef junixsocket_have_sun_len
		  su.sun_len = (sizeof(su) - sizeof(su.sun_path) + strlen(su.sun_path));
		#endif
		
		size_t pathlen = 0;
		if ( abstract == JNI_TRUE ) {
			strcpy(su.sun_path+1, socketFile);
			pathlen = strlen(socketFile) + 1;
		} else {
			strcpy(su.sun_path, socketFile);
			pathlen = strlen(socketFile);
		}

		(*env)->ReleaseStringUTFChars(env, file, socketFile);
		
		socklen_t suLength = pathlen + sizeof(su.sun_family)
		#ifdef junixsocket_have_sun_len
		 + sizeof(su.sun_len)
	        #endif
        	;

		
		int ret = connect(socketHandle, (struct sockaddr *)&su, suLength);
		if(ret == -1) {
            close(socketHandle);
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}
	
		org_newsclub_net_unix_NativeUnixSocket_initFD(env, fd, socketHandle);
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    bind
	 * Signature: (Ljava/lang/String;Ljava/io/FileDescriptor;I)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_bind
	(JNIEnv * env, jclass clazz, jstring file, jobject fd, jint backlog) {
		const char* socketFile = (*env)->GetStringUTFChars(env, file, NULL);
		if(socketFile == NULL) {
			return; // OOME
		}
		if(strlen(socketFile) >= 104) {
			(*env)->ReleaseStringUTFChars(env, file, socketFile);
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Pathname too long for socket", file);
			return;
		}
		
		int serverHandle = socket(AF_UNIX, SOCK_STREAM, 0);
		if(serverHandle == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env,strerror(errno), file);
			return;
		}
		
		struct sockaddr_un su;
		su.sun_family = AF_UNIX;
		#ifndef __linux__
		  su.sun_len = (sizeof(su) - sizeof(su.sun_path) + strlen(su.sun_path));
		#endif
		
		strcpy(su.sun_path, socketFile);
		(*env)->ReleaseStringUTFChars(env, file, socketFile);
		
		socklen_t suLength = strlen(su.sun_path) + sizeof(su.sun_family)
		#ifndef __linux__
		 + sizeof(su.sun_len)
		 #endif
		 ;
		
		int bindRes = bind(serverHandle, (struct sockaddr *)&su, suLength);
		
		if(bindRes == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}
		
		int chmodRes = chmod(su.sun_path, 0666);
		if(chmodRes == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
		}
		
		int listenRes = listen(serverHandle, backlog);
		if(listenRes == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}
		
		org_newsclub_net_unix_NativeUnixSocket_initFD(env, fd, serverHandle);
	}		
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    read
	 * Signature: (Ljava/io/FileDescriptor;[BII)I
	 */
	JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_read
	(JNIEnv * env, jclass clazz, jobject fd, jbyteArray jbuf, jint offset, jint length) {
		jbyte *buf = (*env)->GetByteArrayElements(env, jbuf, NULL);
		if(buf == NULL) {
			return -1; // OOME
		}

		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		
		ssize_t count = read(handle, &(buf[offset]), length);
		(*env)->ReleaseByteArrayElements(env, jbuf, buf, 0);
		
        if(count == 0) {
            // read(2) returns 0 on EOF. Java returns -1.
            return -1;
        } else if(count == -1) {
            // read(2) returns -1 on error. Java throws an Exception.
            
//          Removed since non-blocking is not yet supported
//			if(errno == EAGAIN || errno == EWOULDBLOCK) {
//				return 0;
//			}
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			return -1;
		}
		
		return count;
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    close
	 * Signature: (Ljava/io/FileDescriptor;)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_close
	(JNIEnv * env, jclass clazz, jobject fd) {
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		int ret = close(handle);
		if(ret == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
		}
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    getSocketOptionInt
	 * Signature: (Ljava/io/FileDescriptor;I)I
	 */
	JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_getSocketOptionInt
	(JNIEnv * env, jclass clazz, jobject fd, jint optID) {
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
        
        optID = convertSocketOptionToNative(optID);
        if(optID == -1) {
            org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Unsupported socket option", NULL);
            return -1;
        }
		
        if(optID == SO_SNDTIMEO || optID == SO_RCVTIMEO) {
			struct timeval optVal;
			socklen_t optLen = sizeof(optVal);
			int ret = getsockopt(handle, SOL_SOCKET, optID, &optVal, &optLen);
			if(ret == -1) {
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
				return -1;
			}
			return optVal.tv_sec * 1000 + optVal.tv_usec / 1000;
		} else if(optID == SO_LINGER) {
			struct linger optVal;
			socklen_t optLen = sizeof(optVal);
			
			int ret = getsockopt(handle, SOL_SOCKET, optID, &optVal, &optLen);
			if(ret == -1) {
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
				return -1;
			}
			if(optVal.l_onoff == 0) {
				return -1;
			} else {
				return optVal.l_linger;
			}
		}
		
		int optVal;
		socklen_t optLen = sizeof(optVal);
		
		int ret = getsockopt(handle, SOL_SOCKET, optID, &optVal, &optLen);
		if(ret == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			return -1;
		}
		
		return optVal;
	}
	void org_newsclub_net_unix_NativeUnixSocket_initFD(JNIEnv * env, jobject fd, int handle) {
		jclass fileDescriptorClass = (*env)->GetObjectClass(env, fd);
		jfieldID fdField = (*env)->GetFieldID(env, fileDescriptorClass, "fd", "I");
		if(fdField == NULL) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Cannot find field \"fd\" in java.io.FileDescriptor. Unsupported JVM?", NULL);
			return;
		}
		(*env)->SetIntField(env, fd, fdField, handle);
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    setBoundServer
	 * Signature: (Lorg/newsclub/net/unix/AFUNIXServerSocket;)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_setBoundServer
	(JNIEnv * env, jclass clazz, jobject socket) {
		jclass socketClass = (*env)->GetObjectClass(env, socket);
		jmethodID methodID = (*env)->GetMethodID(env, socketClass, "setBound", "()V");
		if(methodID == NULL) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Cannot find method \"setBound\" in java.net.ServerSocket. Unsupported JVM?", NULL);
			return;
		}
		(*env)->CallVoidMethod(env, socket, methodID);
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    initServerImpl
	 * Signature: (Lcom/newsclub/net/unix/AFUNIXServerSocket;Lcom/newsclub/net/unix/AFUNIXSocketImpl;)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_initServerImpl
	(JNIEnv * env, jclass clazz, jobject serverSocket, jobject impl) {
		jclass serverSocketClass = (*env)->GetObjectClass(env, serverSocket);
		jfieldID fieldID = (*env)->GetFieldID(env, serverSocketClass, "impl", "Ljava/net/SocketImpl;");
		if(fieldID == NULL) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Cannot find field \"impl\" in java.net.SocketImpl. Unsupported JVM?", NULL);
			return;
		}
		(*env)->SetObjectField(env, serverSocket, fieldID, impl);
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    listen
	 * Signature: (Ljava/io/FileDescriptor;I)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_listen
	(JNIEnv * env, jclass clazz, jobject fd, jint backlog) {
		int serverHandle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);

		int listenRes = listen(serverHandle, backlog);
		if(listenRes == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			return;
		}
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    setPort
	 * Signature: (Lorg/newsclub/net/unix/AFUNIXSocketAddress;I)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_setPort
	(JNIEnv * env, jclass clazz, jobject addr, jint port) {
		jclass fileDescriptorClass = (*env)->GetObjectClass(env, addr);
		jfieldID portField = (*env)->GetFieldID(env, fileDescriptorClass, "port", "I");
		if(portField == NULL) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Cannot find field \"port\" in java.net.InetSocketAddress. Unsupported JVM?", NULL);
			return;
		}
		(*env)->SetIntField(env, addr, portField, port);
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    shutdown
	 * Signature: (Ljava/io/FileDescriptor;I)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_shutdown
	(JNIEnv * env, jclass clazz, jobject fd, jint mode) {
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		int ret = shutdown(handle, mode);
		if(ret == -1) {
			if(errno == ENOTCONN) {
				// ignore
				return;
			}
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
		}
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    accept
	 * Signature: (Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_accept
	(JNIEnv * env, jclass clazz, jstring file, jobject fdServer, jobject fd) {
		
		const char* socketFile = (*env)->GetStringUTFChars(env, file, NULL);
		if(socketFile == NULL) {
			return; // OOME
		}
		if(strlen(socketFile) >= 104) {
			(*env)->ReleaseStringUTFChars(env, file, socketFile);
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Pathname too long for socket", file);
			return;
		}
		
		int serverHandle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fdServer);
		
		struct sockaddr_un su;
		su.sun_family = AF_UNIX;
#ifndef __linux__
		su.sun_len = (sizeof(su) - sizeof(su.sun_path) + strlen(su.sun_path));
#endif
		strcpy(su.sun_path, socketFile);
		(*env)->ReleaseStringUTFChars(env, file, socketFile);
		
		socklen_t suLength = strlen(su.sun_path) + sizeof(su.sun_family)
#ifndef __linux__
		+ sizeof(su.sun_len)
#endif
		;
		
		int socketHandle = accept(serverHandle, (struct sockaddr *)&su, &suLength);
		if(socketHandle == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}
		
		org_newsclub_net_unix_NativeUnixSocket_initFD(env, fd, socketHandle);
		return;
	}
Exemple #14
0
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    available
	 * Signature: (Ljava/io/FileDescriptor;)I
	 */
	JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_available
	(JNIEnv * env, jclass clazz, jobject fd) {
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		
        // the following would actually block and keep the peek'ed byte in the buffer
		//ssize_t count = recv(handle, &buf, 1, MSG_PEEK);
        
        int count;
        ioctl(handle, FIONREAD, &count);
		if(count == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			return -1;
		}
		
		return count;
	}
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    write
	 * Signature: (Ljava/io/FileDescriptor;[BII)I
	 */
	JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_write
	(JNIEnv * env, jclass clazz, jobject fd, jbyteArray jbuf, jint offset, jint length) {
		jbyte *buf = (*env)->GetByteArrayElements(env, jbuf, NULL);
		if(buf == NULL) {
			return -1; // OOME
		}
		int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);

		ssize_t count = write(handle, &buf[offset], length);
		(*env)->ReleaseByteArrayElements(env, jbuf, buf, 0);

		if(count == -1) {
			if(errno == EAGAIN || errno == EWOULDBLOCK) {
				return 0;
			}
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
			return -1;
		}
		
		return count;	
	}
Exemple #16
0
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    sendCredentials
	 * Signature: (Ljava/io/FileDescriptor;B)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sendCredentials
	  (JNIEnv *env, jclass clazz, jobject fd, jbyte data) {
		  int handle = org_newsclub_net_unix_NativeUnixSocket_getFD(env, fd);
		  struct msghdr msg;
		  struct iovec iov;
		  msg.msg_name = NULL;
		  msg.msg_namelen = 0;
		  msg.msg_flags = 0;
		  msg.msg_iov = &iov;
		  msg.msg_iovlen = 1;
		  msg.msg_control = NULL;
		  msg.msg_controllen = 0;
		  iov.iov_base = &data;
		  iov.iov_len = 1;

#ifdef SCM_CREDENTIALS
		  char buf[CMSG_SPACE(sizeof(struct ucred))];
		  msg.msg_control = buf;
		  msg.msg_controllen = sizeof buf;
		  struct cmsghdr *cmsg;
		  struct ucred *creds;

		  cmsg = CMSG_FIRSTHDR(&msg);
		  cmsg->cmsg_level = SOL_SOCKET;
		  cmsg->cmsg_type = SCM_CREDENTIALS;
		  cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
		  /* Initialize the payload: */
		  creds = (struct ucred *)CMSG_DATA(cmsg);
		  creds->pid = getpid();
		  creds->uid = getuid();
		  creds->gid = getgid();
#endif

		  int rv = sendmsg(handle, &msg, 0);
		  if (-1 == rv) {
		  	org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(rv), NULL);
		  }
	}
Exemple #17
0
	/*
	 * Class:     org_newsclub_net_unix_NativeUnixSocket
	 * Method:    bind
	 * Signature: (Ljava/lang/String;Ljava/io/FileDescriptor;I)V
	 */
	JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_bind
	(JNIEnv * env, jclass clazz, jstring file, jobject fd, jint backlog, jboolean abstract, jboolean dgram) {
		const char* socketFile = (*env)->GetStringUTFChars(env, file, NULL);
		if(socketFile == NULL) {
			return; // OOME
		}
		if(strlen(socketFile) >= 104) {
			(*env)->ReleaseStringUTFChars(env, file, socketFile);
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, "Pathname too long for socket", file);
			return;
		}
		int sockType = SOCK_STREAM;

		if ( dgram == JNI_TRUE ) {
			sockType = SOCK_DGRAM;
		}
		
		int serverHandle = socket(AF_UNIX, sockType, 0);
		if(serverHandle == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env,strerror(errno), file);
			return;
		}
        

        	// This block is only prophylactic, as SO_REUSEADDR seems not to work with AF_UNIX
	        int optVal = 1;
		int ret = setsockopt(serverHandle, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(optVal));
		if(ret == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), NULL);
		}        
		
		struct sockaddr_un su;
		memset(&su, 0, sizeof(struct sockaddr_un));
		su.sun_family = AF_UNIX;
		#ifdef junixsocket_have_sun_len
		  su.sun_len = (sizeof(su) - sizeof(su.sun_path) + strlen(su.sun_path));
		#endif
		
		size_t pathlen = 0;
		if ( abstract == JNI_TRUE ) {
			strcpy(su.sun_path+1, socketFile);
			pathlen = strlen(socketFile) + 1;
		} else {
			strcpy(su.sun_path, socketFile);
			pathlen = strlen(socketFile);
		}

		(*env)->ReleaseStringUTFChars(env, file, socketFile);
		
		socklen_t suLength = pathlen + sizeof(su.sun_family)
		#ifdef junixsocket_have_sun_len
		 + sizeof(su.sun_len)
	        #endif
        	;
		
		int bindRes = bind(serverHandle, (struct sockaddr *)&su, suLength);
		
		if(bindRes == -1) {
			int myErr = errno;
			if(errno == EADDRINUSE) {
				// Let's check whether the address *really* is in use.
				// Maybe it's just a dead reference

				// if the given file exists, but is not a socket, ENOTSOCK is returned
				// if access is denied, EACCESS is returned
				ret = connect(serverHandle, (struct sockaddr *)&su, suLength);

				if(ret == -1 && errno == ECONNREFUSED) {
					// assume non-connected socket

					close(serverHandle);
					if(unlink(socketFile) == -1) {
						org_newsclub_net_unix_NativeUnixSocket_throwException(env,strerror(errno), file);
						return;
					}

					serverHandle = socket(AF_UNIX, SOCK_STREAM, 0);
					if(serverHandle == -1) {
						org_newsclub_net_unix_NativeUnixSocket_throwException(env,strerror(errno), file);
						return;
					}

					bindRes = bind(serverHandle, (struct sockaddr *)&su, suLength);
					if(bindRes == -1) {
						close(serverHandle);
						org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(myErr), file);
						return;
					}
				} else {
					close(serverHandle);
					org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(myErr), file);
					return;
				}
			} else {
				close(serverHandle);
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(myErr), file);
				return;
			}
		}

		if ( abstract == JNI_FALSE ) {
			int chmodRes = chmod(su.sun_path, 0666);
			if(chmodRes == -1) {
				org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			}
		}
		
		int listenRes = listen(serverHandle, backlog);
		if(listenRes == -1) {
			org_newsclub_net_unix_NativeUnixSocket_throwException(env, strerror(errno), file);
			return;
		}
		
		org_newsclub_net_unix_NativeUnixSocket_initFD(env, fd, serverHandle);
	}