diff options
| author | Matthew Finkel <Matthew.Finkel@gmail.com> | 2018-09-20 21:14:26 +0000 |
|---|---|---|
| committer | Matthew Finkel <Matthew.Finkel@gmail.com> | 2018-09-20 21:21:14 +0000 |
| commit | 731b7e2b176f7cf6439c1286886492998e58cd7f (patch) | |
| tree | 88b0615e5cdcd7e5856a7e42d2be025d5ea27d37 | |
| parent | 621ef35eb5464bcf63857deebd2be6728483e8b6 (diff) | |
Ticket 26653: Add Java exampletesting_26653
Compile the Java source files and generate the JNI C/C++ header:
$ javac -h src/feature/api/ src/tools/jverify_config.java src/feature/api/TorApi.java
Compile the shared library like:
$ g++ -Wall -Werror -g -fPIC -I"/usr/lib/jvm/java-9-openjdk-9.0.4.11-4.fc26.x86_64/include" -I"/usr/lib/jvm/java-9-openjdk-9.0.4.11-4.fc26.x86_64/include/linux" -shared -o libtor.so src/feature/api/TorApi.cc -lpthread -lz -lm -levent -lssl -lcrypto -ldl `make show-libs`
(Adjust the JDK include paths, as needed)
Run it like:
$ java -Djava.library.path=. -classpath src/feature/:src/ tools.jverify_config --Log "notice stdout" --AvoidDiskWrites 1 --SocksPort unix:${HOME}/test_socks --DisableNetwork 0
| -rw-r--r-- | src/feature/api/TorApi.cc | 293 | ||||
| -rw-r--r-- | src/feature/api/TorApi.java | 21 | ||||
| -rw-r--r-- | src/feature/api/api_TorApi.h | 77 | ||||
| -rw-r--r-- | src/tools/jverify_config.java | 57 |
4 files changed, 448 insertions, 0 deletions
diff --git a/src/feature/api/TorApi.cc b/src/feature/api/TorApi.cc new file mode 100644 index 000000000..9b3df1605 --- /dev/null +++ b/src/feature/api/TorApi.cc @@ -0,0 +1,293 @@ +extern "C" { + +#include "tor_api.h" +#include "api_TorApi.h" + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +} + +#include <string> + +extern "C" { +static char **argv = NULL; + +static jfieldID GetConfigurationFieldID(JNIEnv * env, jclass torApiClass) { + return env->GetFieldID(torApiClass, "torConfiguration", "J"); +} + +static jlong GetConfigurationObject(JNIEnv * env, jobject thisObj) { + jclass torApiClass = env->GetObjectClass(thisObj); + if (torApiClass == NULL) { + fprintf(stderr, "GetObjectClass returned NULL\n"); + return 0; + } + + jfieldID torConfigurationField = GetConfigurationFieldID(env, torApiClass); + if (torConfigurationField == NULL) { + fprintf(stderr, "The fieldID is NULL\n"); + return 0; + } + + jlong torConfiguration = env->GetLongField(thisObj, torConfigurationField); + + return torConfiguration; +} + +static bool SetConfiguration(JNIEnv * env, jobject thisObj, const tor_main_configuration_t* torConfiguration) { + jclass torApiClass = env->GetObjectClass(thisObj); + if (torApiClass == NULL) { + return NULL; + } + + jfieldID torConfigurationField = GetConfigurationFieldID(env, torApiClass); + if (torConfigurationField == NULL) { + return NULL; + } + + jlong torCfg = reinterpret_cast<jlong>(torConfiguration); + + env->SetLongField(thisObj, torConfigurationField, torCfg); + return true; +} + +static tor_main_configuration_t* GetConfiguration(JNIEnv * env, jobject thisObj) { + jlong torConfiguration = GetConfigurationObject(env, thisObj); + if (torConfiguration == 0) { + fprintf(stderr, "The long is 0\n"); + return NULL; + } + + tor_main_configuration_t *torCfg = reinterpret_cast<tor_main_configuration_t *>(torConfiguration); + + return torCfg; +} + +static jfieldID GetControlSocketFieldID(JNIEnv * const env, jclass torApiClass) { + return env->GetFieldID(torApiClass, "torControlFd", "I"); +} + +static bool SetControlSocket(JNIEnv * env, jobject thisObj, int socket) { + jclass torApiClass = env->GetObjectClass(thisObj); + if (torApiClass == NULL) { + fprintf(stderr, "SetControlSocket: GetObjectClass returned NULL\n"); + return false; + } + + jfieldID controlFieldId = GetControlSocketFieldID(env, torApiClass); + + env->SetIntField(thisObj, controlFieldId, socket); + return true; +} + +static int GetControlSocket(JNIEnv * const env, jobject thisObj) { + jclass torApiClass = env->GetObjectClass(thisObj); + if (torApiClass == NULL) { + fprintf(stderr, "GetControlSocket: GetObjectClass returned NULL\n"); + return 0; + } + + jfieldID controlFieldId = GetControlSocketFieldID(env, torApiClass); + + return env->GetIntField(thisObj, controlFieldId); +} + +static bool CreateTorConfiguration(JNIEnv * env, jobject thisObj) { + jlong torConfiguration = GetConfigurationObject(env, thisObj); + if (torConfiguration == 0) { + return false; + } + + tor_main_configuration_t *tor_config = tor_main_configuration_new(); + if (tor_config == NULL) { + fprintf(stderr, "Allocating and creating a new configuration structure failed.\n"); + return false; + } + + if (!SetConfiguration(env, thisObj, tor_config)) { + tor_main_configuration_free(tor_config); + return false; + } + + return true; +} + +static bool SetCommandLine(JNIEnv * env, jobject thisObj, jobjectArray arrArgv) { + tor_main_configuration_t *torCfg = GetConfiguration(env, thisObj); + if (torCfg == NULL) { + fprintf(stderr, "SetCommandLine: The Tor configuration is NULL!\n"); + return -1; + } + + jsize arrArgvLen = env->GetArrayLength(arrArgv); + if (arrArgvLen > (INT_MAX-1)) { + fprintf(stderr, "Too many args\n"); + return false; + } + int argc = static_cast<int>(arrArgvLen); + + argv = new char*[argc]; + if (argv == NULL) { + return false; + } + + for (jsize i=0; i<argc; i++) { + jobject objElm = env->GetObjectArrayElement(arrArgv, i); + jstring argElm = static_cast<jstring>(objElm); + const char *arg = env->GetStringUTFChars(argElm, NULL); + argv[i] = strdup(arg); + } + + if (tor_main_configuration_set_command_line(torCfg, argc, argv)) { + fprintf(stderr, "Setting the command line config failed\n"); + return false; + } + return true; +} + +static int SetupControlSocket(JNIEnv * env, jobject thisObj) { + jclass torApiClass = env->GetObjectClass(thisObj); + if (torApiClass == NULL) { + fprintf(stderr, "SetupControlSocket: GetObjectClass returned NULL\n"); + return false; + } + + tor_main_configuration_t *torCfg = GetConfiguration(env, thisObj); + if (torCfg == NULL) { + fprintf(stderr, "SetupControlSocket: The Tor configuration is NULL!\n"); + return false; + } + + tor_control_socket_t socket = tor_main_configuration_setup_control_socket(torCfg); + fcntl(socket, F_SETFL, O_NONBLOCK); + SetControlSocket(env, thisObj, socket); + return true; +} + +static int RunMain(JNIEnv *env, jobject thisObj) { + tor_main_configuration_t *torCfg = GetConfiguration(env, thisObj); + if (torCfg == NULL) { + fprintf(stderr, "RunMain: The Tor configuration is NULL!\n"); + return -1; + } + + int rv = tor_run_main(torCfg); + if (rv != 0) { + fprintf(stderr, "Tor returned with an error\n"); + } else { + printf("Tor returned successfully\n"); + } + return rv; +} + +static bool ControlSocketRead(JNIEnv * env, jobject thisObj, std::string& entirebuf) { + ssize_t did_read = 0; + int sock = GetControlSocket(env, thisObj); + char buf[101], *buf_p; + size_t len; + struct pollfd pfd[1]; + int ready; + + len = sizeof(buf)/sizeof(buf[0]) - 1; + + pfd[0].fd = sock; + pfd[0].events = POLLIN; + + printf("Reading control socket\n"); + if ((ready = poll(pfd, 1, did_read?0:-1)) && ready > 0) { + buf_p = buf; + memset(buf, 0, len); + do { + len -= did_read; + buf_p += did_read; + did_read = read(sock, buf_p, len); + if (did_read == -1) { + if (errno != EAGAIN) { + printf("Read error: '%s'\n", strerror(errno)); + } + } else { + printf("Read %lu bytes\n", did_read); + } + } while (did_read > 0); + + if (did_read == -1 && errno != EAGAIN) { + return false; + } + + if (len == 0) { + entirebuf.append(buf); + did_read = 0; + len = 100; + } + entirebuf.append(buf); + } else { + return false; + } + + return true; +} + +static bool ControlSocketWrite(JNIEnv * env, jobject thisObj, jstring buf) { + ssize_t wrote = 0; + const char * outBuf = env->GetStringUTFChars(buf, NULL); + size_t len = env->GetStringLength(buf); + int sock = GetControlSocket(env, thisObj); + do { + len -= wrote; + buf += wrote; + wrote = write(sock, outBuf, len); + } while (wrote > 0); + + if (wrote == -1) { + return false; + } + + return true; +} + +JNIEXPORT jboolean JNICALL Java_api_TorApi_createTorConfiguration(JNIEnv * env, jobject thisObj) { + return CreateTorConfiguration(env, thisObj); +} + +JNIEXPORT jboolean JNICALL Java_api_TorApi_mainConfigurationSetCommandLine(JNIEnv * env, jobject thisObj, jobjectArray arrArgv) { + return SetCommandLine(env, thisObj, arrArgv); +} + +JNIEXPORT jboolean JNICALL Java_api_TorApi_mainConfigurationSetupControlSocket(JNIEnv *env, jobject thisObj) { + return SetupControlSocket(env, thisObj); +} + +JNIEXPORT void JNICALL Java_api_TorApi_mainConfigurationFree(JNIEnv *env, jobject thisObj) { + tor_main_configuration_t *torCfg = GetConfiguration(env, thisObj); + if (torCfg == NULL) { + fprintf(stderr, "ConfigurationFree: The Tor configuration is NULL!\n"); + return; + } + tor_main_configuration_free(torCfg); +} + +JNIEXPORT jstring JNICALL Java_api_TorApi_apiGetProviderVersion(JNIEnv * env, jobject thisObj) { + return env->NewStringUTF(tor_api_get_provider_version()); +} + +JNIEXPORT jint JNICALL Java_api_TorApi_runMain(JNIEnv *env, jobject thisObj) { + return RunMain(env, thisObj); +} + +JNIEXPORT jstring JNICALL Java_api_TorApi_ControlSocketRead(JNIEnv * env, jobject thisObj) { + std::string buf; + ControlSocketRead(env, thisObj, buf); + + return env->NewStringUTF(buf.c_str()); +} + +JNIEXPORT jboolean JNICALL Java_api_TorApi_ControlSocketWrite(JNIEnv * env, jobject thisObj, jstring buf) { + return ControlSocketWrite(env, thisObj, buf); +} + +} // "C" diff --git a/src/feature/api/TorApi.java b/src/feature/api/TorApi.java new file mode 100644 index 000000000..b3ae85e41 --- /dev/null +++ b/src/feature/api/TorApi.java @@ -0,0 +1,21 @@ +package api; + +public class TorApi { + static { + System.loadLibrary("tor"); + } + + // Store the opaque reference as a long (pointer) + private long torConfiguration = -1; + private int torControlFd = -1; + + public native boolean createTorConfiguration(); + public native boolean mainConfigurationSetCommandLine(String[] argv); + public native boolean mainConfigurationSetupControlSocket(); + public native void mainConfigurationFree(); + public native String apiGetProviderVersion(); + public native int runMain(); + public native String ControlSocketRead(); + public native boolean ControlSocketWrite(String buf); + +} diff --git a/src/feature/api/api_TorApi.h b/src/feature/api/api_TorApi.h new file mode 100644 index 000000000..05fb04ef5 --- /dev/null +++ b/src/feature/api/api_TorApi.h @@ -0,0 +1,77 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class api_TorApi */ + +#ifndef _Included_api_TorApi +#define _Included_api_TorApi +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: api_TorApi + * Method: createTorConfiguration + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_api_TorApi_createTorConfiguration + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: mainConfigurationSetCommandLine + * Signature: ([Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_api_TorApi_mainConfigurationSetCommandLine + (JNIEnv *, jobject, jobjectArray); + +/* + * Class: api_TorApi + * Method: mainConfigurationSetupControlSocket + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_api_TorApi_mainConfigurationSetupControlSocket + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: mainConfigurationFree + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_api_TorApi_mainConfigurationFree + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: apiGetProviderVersion + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_api_TorApi_apiGetProviderVersion + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: runMain + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_api_TorApi_runMain + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: ControlSocketRead + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_api_TorApi_ControlSocketRead + (JNIEnv *, jobject); + +/* + * Class: api_TorApi + * Method: ControlSocketWrite + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_api_TorApi_ControlSocketWrite + (JNIEnv *, jobject, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/tools/jverify_config.java b/src/tools/jverify_config.java new file mode 100644 index 000000000..8d321020c --- /dev/null +++ b/src/tools/jverify_config.java @@ -0,0 +1,57 @@ +package tools; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.locks.*; +import api.TorApi; + +public class jverify_config { + public static void main(String[] args) throws InterruptedException { + final Lock lock = new ReentrantLock(); + final Condition controlSocketReady = lock.newCondition(); + final TorApi tor = new TorApi(); + tor.createTorConfiguration(); + System.out.println("Running with " + tor.apiGetProviderVersion()); + + new Thread() { + public void run() { + ArrayList<String> verifyArgs = new ArrayList(args.length + 2); + verifyArgs.addAll(Arrays.asList(args)); + verifyArgs.add(0, "jverify_config"); + verifyArgs.add(1, "--verify-config"); + if (!tor.mainConfigurationSetCommandLine(verifyArgs.toArray(new String[0]))) { + System.out.println("Setting command line failed"); + return; + } + if (!tor.mainConfigurationSetupControlSocket()) { + System.out.println("Setting control socket failed"); + return; + } + + lock.lock(); + controlSocketReady.signal(); + lock.unlock(); + + tor.runMain(); + ArrayList<String> argv = new ArrayList(args.length + 1); + argv.addAll(Arrays.asList(args)); + argv.add(0, "jverify_config"); + + if (!tor.mainConfigurationSetCommandLine(argv.toArray(new String[0]))) { + System.out.println("Setting command line failed"); + return; + } + tor.runMain(); + } + }.start(); + + lock.lock(); + controlSocketReady.await(); + System.out.println("Writing control socket"); + tor.ControlSocketWrite("PROTOCOLINFO\r\n"); + System.out.println("Waiting on response"); + System.out.println(tor.ControlSocketRead()); + tor.ControlSocketWrite("SIGNAL TERM\r\n"); + lock.unlock(); + } +} |
