diff --git a/Makefile b/Makefile index 48a1208..eec7e33 100644 --- a/Makefile +++ b/Makefile @@ -141,15 +141,14 @@ endif # JNI (Java Native Interface) ifeq ($(SDK_JNI), 1) # jni.h - INCLUDES+=-I$(shell /usr/libexec/java_home)/include + LIBZT_INCLUDES+=-I$(shell /usr/libexec/java_home)/include # jni_md.h - INCLUDES+=-I$(shell /usr/libexec/java_home)/include/$(SYSTEM) - CXXFLAGS+=-DSDK_JNI + LIBZT_INCLUDES+=-I$(shell /usr/libexec/java_home)/include/$(OSTYPE) + LIBZT_DEFS+=-DSDK_JNI endif CXXFLAGS=$(CFLAGS) -Wno-format -fno-rtti -std=c++11 ZT_DEFS+=-DZT_SDK -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\"" -LIBZT_DEFS+= LIBZT_FILES:=src/VirtualTap.cpp src/libzt.cpp src/Utilities.cpp STATIC_LIB=$(BUILD)/libzt.a diff --git a/src/ZT1Service.cpp b/src/ZT1Service.cpp index 8ab73e6..18dc10f 100644 --- a/src/ZT1Service.cpp +++ b/src/ZT1Service.cpp @@ -485,6 +485,101 @@ void zts_allow_http_control(bool allowed) // TODO } + + + +#if defined(SDK_JNI) + +namespace ZeroTier { + + #include + + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_ztjni_1start(JNIEnv *env, jobject thisObj, jstring path) { + if (path) { + homeDir = env->GetStringUTFChars(path, NULL); + zts_start(homeDir.c_str()); + } + } + // Shuts down ZeroTier service and SOCKS5 Proxy server + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_ztjni_1stop(JNIEnv *env, jobject thisObj) { + if (ZeroTier::zt1Service) { + zts_stop(); + } + } + + // Returns whether the ZeroTier service is running + JNIEXPORT jboolean JNICALL Java_zerotier_ZeroTier_ztjni_1running( + JNIEnv *env, jobject thisObj) + { + return zts_running(); + } + // Returns path for ZT config/data files + JNIEXPORT jstring JNICALL Java_zerotier_ZeroTier_ztjni_1homepath( + JNIEnv *env, jobject thisObj) + { + // TODO: fix, should copy into given arg + // return (*env).NewStringUTF(zts_get_homepath()); + return (*env).NewStringUTF(""); + } + // Join a network + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_ztjni_1join( + JNIEnv *env, jobject thisObj, jstring nwid) + { + const char *nwidstr; + if (nwid) { + nwidstr = env->GetStringUTFChars(nwid, NULL); + zts_join(nwidstr); + } + } + // Leave a network + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_ztjni_1leave( + JNIEnv *env, jobject thisObj, jstring nwid) + { + const char *nwidstr; + if (nwid) { + nwidstr = env->GetStringUTFChars(nwid, NULL); + zts_leave(nwidstr); + } + } + // FIXME: Re-implemented to make it play nicer with the C-linkage required for Xcode integrations + // Now only returns first assigned address per network. Shouldn't normally be a problem + JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_ztjni_1get_1ipv4_1address( + JNIEnv *env, jobject thisObj, jstring nwid) + { + const char *nwid_str = env->GetStringUTFChars(nwid, NULL); + char address_string[INET_ADDRSTRLEN]; + memset(address_string, 0, INET_ADDRSTRLEN); + zts_get_ipv4_address(nwid_str, address_string, INET_ADDRSTRLEN); + jclass clazz = (*env).FindClass("java/util/ArrayList"); + jobject addresses = (*env).NewObject(clazz, (*env).GetMethodID(clazz, "", "()V")); + jstring _str = (*env).NewStringUTF(address_string); + env->CallBooleanMethod(addresses, env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"), _str); + return addresses; + } + + JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_ztjni_1get_1ipv6_1address( + JNIEnv *env, jobject thisObj, jstring nwid) + { + const char *nwid_str = env->GetStringUTFChars(nwid, NULL); + char address_string[INET6_ADDRSTRLEN]; + memset(address_string, 0, INET6_ADDRSTRLEN); + zts_get_ipv6_address(nwid_str, address_string, INET6_ADDRSTRLEN); + jclass clazz = (*env).FindClass("java/util/ArrayList"); + jobject addresses = (*env).NewObject(clazz, (*env).GetMethodID(clazz, "", "()V")); + jstring _str = (*env).NewStringUTF(address_string); + env->CallBooleanMethod(addresses, env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"), _str); + return addresses; + } + + // Returns the device is in integer form + JNIEXPORT jint Java_zerotier_ZeroTier_ztjni_1get_1device_1id() + { + return zts_get_device_id(NULL); // TODO + } +} + +#endif // SDK_JNI + #ifdef __cplusplus } #endif diff --git a/src/libzt.cpp b/src/libzt.cpp index 5b71980..b52acce 100644 --- a/src/libzt.cpp +++ b/src/libzt.cpp @@ -496,6 +496,196 @@ int zts_del_dns_nameserver(struct sockaddr *addr) return err; } + +/****************************************************************************/ +/* SDK Socket API (Java Native Interface JNI) */ +/* JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME */ +/****************************************************************************/ + +#if defined(SDK_JNI) + +namespace ZeroTier { + + #include + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1sendto( + JNIEnv *env, jobject thisObj, jint fd, jarray buf, jint len, jint flags, jobject ztaddr) + { + struct sockaddr_in addr; + jclass cls = (*env).GetObjectClass( ztaddr); + jfieldID f = (*env).GetFieldID( cls, "port", "I"); + addr.sin_port = htons((*env).GetIntField( ztaddr, f)); + f = (*env).GetFieldID( cls, "_rawAddr", "J"); + addr.sin_addr.s_addr = (*env).GetLongField( ztaddr, f); + addr.sin_family = AF_INET; + //LOGV("zt_sendto(): fd = %d\naddr = %s\nport=%d", fd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + // TODO: Optimize this + jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); + char * bufp = (char *)malloc(sizeof(char)*len); + memcpy(bufp, body, len); + (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); + // "connect" and send buffer contents + int sent_bytes = zts_sendto(fd, body, len, flags, (struct sockaddr *)&addr, sizeof(addr)); + return sent_bytes; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1recvfrom( + JNIEnv *env, jobject thisObj, jint fd, jbyteArray buf, jint len, jint flags, jobject ztaddr) + { + struct sockaddr_in addr; + jbyte *body = (*env).GetByteArrayElements( buf, 0); + unsigned char buffer[ZT_SDK_MTU]; + int payload_offset = sizeof(int32_t) + sizeof(struct sockaddr_storage); + int rxbytes = zts_recvfrom(fd, &buffer, len, flags, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr_storage)); + if (rxbytes > 0) + memcpy(body, (jbyte*)buffer + payload_offset, rxbytes); + (*env).ReleaseByteArrayElements( buf, body, 0); + // Update fields of Java ZTAddress object + jfieldID fid; + jclass cls = (*env).GetObjectClass( ztaddr); + fid = (*env).GetFieldID( cls, "port", "I"); + (*env).SetIntField( ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID( cls,"_rawAddr", "J"); + (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + return rxbytes; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1send(JNIEnv *env, jobject thisObj, jint fd, jarray buf, jint len, int flags) + { + jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); + char * bufp = (char *)malloc(sizeof(char)*len); + memcpy(bufp, body, len); + (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); + int written_bytes = zts_write(fd, body, len); + return written_bytes; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1write(JNIEnv *env, jobject thisObj, + jint fd, jarray buf, jint len) + { + jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); + char * bufp = (char *)malloc(sizeof(char)*len); + memcpy(bufp, body, len); + (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); + int written_bytes = zts_write(fd, body, len); + return written_bytes; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1read(JNIEnv *env, jobject thisObj, + jint fd, jarray buf, jint len) + { + jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); + int read_bytes = read(fd, body, len); + (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); + return read_bytes; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1setsockopt( + JNIEnv *env, jobject thisObj, + jint fd, jint level, jint optname, jint optval, jint optlen) { + return zts_setsockopt(fd, level, optname, (const void*)optval, optlen); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1getsockopt(JNIEnv *env, jobject thisObj, + jint fd, jint level, jint optname, jint optval, jint optlen) { + return zts_getsockopt(fd, level, optname, (void*)optval, (socklen_t *)optlen); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1socket(JNIEnv *env, jobject thisObj, + jint family, jint type, jint protocol) { + return zts_socket(family, type, protocol); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1connect(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { + struct sockaddr_in addr; + const char *str = (*env).GetStringUTFChars( addrstr, 0); + addr.sin_addr.s_addr = inet_addr(str); + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + (*env).ReleaseStringUTFChars( addrstr, str); + return zts_connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1bind(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { + struct sockaddr_in addr; + const char *str = (*env).GetStringUTFChars( addrstr, 0); + DEBUG_INFO("fd=%d, addr=%s, port=%d", fd, str, port); + addr.sin_addr.s_addr = inet_addr(str); + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + (*env).ReleaseStringUTFChars( addrstr, str); + return zts_bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + } + +#if defined(__linux__) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1accept4(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port, jint flags) { + struct sockaddr_in addr; + char *str; + // = env->GetStringUTFChars(addrstr, NULL); + (*env).ReleaseStringUTFChars( addrstr, str); + addr.sin_addr.s_addr = inet_addr(str); + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + return zts_accept4(fd, (struct sockaddr *)&addr, sizeof(addr), flags); + } +#endif + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1accept(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { + struct sockaddr_in addr; + // TODO: Send addr info back to Javaland + addr.sin_addr.s_addr = inet_addr(""); + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + return zts_accept(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1listen(JNIEnv *env, jobject thisObj, + jint fd, int backlog) { + return zts_listen(fd, backlog); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1close(JNIEnv *env, jobject thisObj, + jint fd) { + return zts_close(fd); + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1getsockname(JNIEnv *env, jobject thisObj, + jint fd, jobject ztaddr) { + struct sockaddr_in addr; + int err = zts_getsockname(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr)); + jfieldID fid; + jclass cls = (*env).GetObjectClass(ztaddr); + fid = (*env).GetFieldID( cls, "port", "I"); + (*env).SetIntField( ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID( cls,"_rawAddr", "J"); + (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + return err; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1getpeername(JNIEnv *env, jobject thisObj, + jint fd, jobject ztaddr) { + struct sockaddr_in addr; + int err = zts_getpeername(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr)); + jfieldID fid; + jclass cls = (*env).GetObjectClass( ztaddr); + fid = (*env).GetFieldID( cls, "port", "I"); + (*env).SetIntField( ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID( cls,"_rawAddr", "J"); + (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + return err; + } + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_ztjni_1fcntl(JNIEnv *env, jobject thisObj, + jint fd, jint cmd, jint flags) { + return zts_fcntl(fd,cmd,flags); + } +} +#endif // SDK_JNI + #ifdef __cplusplus } #endif