Skip to content
Snippets Groups Projects
jtorctl.patch 7.57 KiB
Newer Older
diff --git a/README.md b/README.md
index 902d727..359cc69 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,4 @@
-
 A Java library for controlling a Tor instance via its control port.  It is
 used in the Android app Orbot as well as others.
 
-To add this library to a gradle project:
-
-    compile 'net.freehaven.tor.control:jtorctl:0.2'
+This fork includes patches for managing onion services.
diff --git a/net/freehaven/tor/control/TorControlCommands.java b/net/freehaven/tor/control/TorControlCommands.java
index 36482d5..14486e3 100644
--- a/net/freehaven/tor/control/TorControlCommands.java
+++ b/net/freehaven/tor/control/TorControlCommands.java
@@ -144,5 +144,8 @@ public interface TorControlCommands {
         "No such OR",
     };
 
+    public static final String HS_ADDRESS = "onionAddress";
+    public static final String HS_PRIVKEY = "onionPrivKey";
+
 }
 
diff --git a/net/freehaven/tor/control/TorControlConnection.java b/net/freehaven/tor/control/TorControlConnection.java
index 9524612..eacdd66 100644
--- a/net/freehaven/tor/control/TorControlConnection.java
+++ b/net/freehaven/tor/control/TorControlConnection.java
@@ -40,10 +40,14 @@ public class TorControlConnection implements TorControlCommands {
     static class Waiter {
     
         List<ReplyLine> response; // Locking: this
+        boolean interrupted;
     
         synchronized List<ReplyLine> getResponse() throws InterruptedException {
                 while (response == null) {
                     wait();
+                    if (interrupted) {
+                        throw new InterruptedException();
+                    }
                 }
             return response;
         }
@@ -52,6 +56,11 @@ public class TorControlConnection implements TorControlCommands {
             this.response = response;
             notifyAll();
         }
+
+        synchronized void interrupt() {
+            interrupted = true;
+            notifyAll();
+        }
     }
 
     static class ReplyLine {
@@ -171,7 +180,7 @@ public class TorControlConnection implements TorControlCommands {
 
     protected synchronized List<ReplyLine> sendAndWaitForResponse(String s,
         String rest) throws IOException {
-        if(parseThreadException != null) throw parseThreadException;
+        if (parseThreadException != null) throw parseThreadException;
         checkThread();
         Waiter w = new Waiter();
         if (debugOutput != null)
@@ -305,8 +314,15 @@ public class TorControlConnection implements TorControlCommands {
         while (true) {
             ArrayList<ReplyLine> lst = readReply();
             if (lst.isEmpty()) {
-                // connection has been closed remotely! end the loop!
-                return;
+                // interrupted queued waiters, there won't be any response.
+                synchronized (waiters) {
+                    if (!waiters.isEmpty()) {
+                        for (Waiter w : waiters) {
+                            w.interrupt();
+                        }
+                    }
+                }
+                throw new IOException("Tor is no longer running");
             }
             if ((lst.get(0)).status.startsWith("6"))
                 handleEvent(lst);
@@ -736,11 +752,111 @@ public class TorControlConnection implements TorControlCommands {
         sendAndWaitForResponse("TAKEOWNERSHIP\r\n", null);
     }
 
+    /**
+     * Tells Tor to generate and set up a new onion service using the best
+     * supported algorithm.
+     * <p/>
+     * ADD_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public Map<String,String> addOnion(Map<Integer,String> portLines)
+                                       throws IOException {
+        return addOnion("NEW:BEST", portLines, null);
+    }
+
+    /**
+     * Tells Tor to generate and set up a new onion service using the best
+     * supported algorithm.
+     * <p/>
+     * ADD_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public Map<String,String> addOnion(Map<Integer,String> portLines,
+                                       boolean ephemeral, boolean detach)
+                                       throws IOException {
+        return addOnion("NEW:BEST", portLines, ephemeral, detach);
+    }
+
+    /**
+     * Tells Tor to set up an onion service using the provided private key.
+     * <p/>
+     * ADD_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public Map<String,String> addOnion(String privKey,
+                                       Map<Integer,String> portLines)
+                                       throws IOException {
+        return addOnion(privKey, portLines, null);
+    }
+
+    /**
+     * Tells Tor to set up an onion service using the provided private key.
+     * <p/>
+     * ADD_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public Map<String,String> addOnion(String privKey,
+                                       Map<Integer,String> portLines,
+                                       boolean ephemeral, boolean detach)
+                                       throws IOException {
+        List<String> flags = new ArrayList<String>();
+        if (ephemeral)
+            flags.add("DiscardPK");
+        if (detach)
+            flags.add("Detach");
+        return addOnion(privKey, portLines, flags);
+    }
+
+    /**
+     * Tells Tor to set up an onion service.
+     * <p/>
+     * ADD_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public Map<String,String> addOnion(String privKey,
+                                       Map<Integer,String> portLines,
+                                       List<String> flags)
+                                       throws IOException {
+        if (privKey.indexOf(':') < 0)
+            throw new IllegalArgumentException("Invalid privKey");
+        if (portLines == null || portLines.size() < 1)
+            throw new IllegalArgumentException("Must provide at least one port line");
+        StringBuilder b = new StringBuilder();
+        b.append("ADD_ONION ").append(privKey);
+        if (flags != null && flags.size() > 0) {
+            b.append(" Flags=");
+            String separator = "";
+            for (String flag : flags) {
+                b.append(separator).append(flag);
+                separator = ",";
+            }
+        }
+        for (Map.Entry<Integer,String> portLine : portLines.entrySet()) {
+            int virtPort = portLine.getKey();
+            String target = portLine.getValue();
+            b.append(" Port=").append(virtPort);
+            if (target != null && target.length() > 0)
+                b.append(",").append(target);
+        }
+        b.append("\r\n");
+        List<ReplyLine> lst = sendAndWaitForResponse(b.toString(), null);
+        Map<String,String> ret = new HashMap<String,String>();
+        ret.put(HS_ADDRESS, (lst.get(0)).msg.split("=", 2)[1]);
+        if (lst.size() > 2)
+            ret.put(HS_PRIVKEY, (lst.get(1)).msg.split("=", 2)[1]);
+        return ret;
+    }
+
+    /**
+     * Tells Tor to take down an onion service previously set up with
+     * addOnion(). The hostname excludes the .onion extension.
+     * <p/>
+     * DEL_ONION was added in Tor 0.2.7.1-alpha.
+     */
+    public void delOnion(String hostname) throws IOException {
+        sendAndWaitForResponse("DEL_ONION " + hostname + "\r\n", null);
+    }
+
     /** Tells Tor to forget any cached client state relating to the hidden
      * service with the given hostname (excluding the .onion extension).
      */
     public void forgetHiddenService(String hostname) throws IOException {
-        sendAndWaitForResponse("FORGETHS " + hostname + "\r\n", null);
+        sendAndWaitForResponse("HSFORGET " + hostname + "\r\n", null);