summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Finkel <Matthew.Finkel@gmail.com>2018-09-20 21:14:26 +0000
committerMatthew Finkel <Matthew.Finkel@gmail.com>2018-09-20 21:21:14 +0000
commit731b7e2b176f7cf6439c1286886492998e58cd7f (patch)
tree88b0615e5cdcd7e5856a7e42d2be025d5ea27d37
parent621ef35eb5464bcf63857deebd2be6728483e8b6 (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.cc293
-rw-r--r--src/feature/api/TorApi.java21
-rw-r--r--src/feature/api/api_TorApi.h77
-rw-r--r--src/tools/jverify_config.java57
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();
+ }
+}