[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] [puppetor/gsoc2008] Convert line endings and file access mask.
644 sounds saner than 755, and \n is nicer than \r\n.
---
LICENSE | 222 +-
README | 16 +-
logging.properties | 8 +-
.../wiai/lspi/puppetor/ClientApplication.java | 252 ++--
src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java | 150 +-
src/de/uniba/wiai/lspi/puppetor/EventManager.java | 514 +++---
src/de/uniba/wiai/lspi/puppetor/EventType.java | 104 +-
src/de/uniba/wiai/lspi/puppetor/Network.java | 1366 ++++++------
.../uniba/wiai/lspi/puppetor/NetworkFactory.java | 216 +-
src/de/uniba/wiai/lspi/puppetor/NodeState.java | 140 +-
src/de/uniba/wiai/lspi/puppetor/ProxyNode.java | 590 +++---
.../wiai/lspi/puppetor/PuppeTorException.java | 182 +-
src/de/uniba/wiai/lspi/puppetor/RouterNode.java | 162 +-
.../wiai/lspi/puppetor/ServerApplication.java | 168 +-
.../examples/AccessingPublicWebServerOverTor.java | 290 ++--
...ccessingHiddenServiceOverPrivateTorNetwork.java | 316 ++--
...AccessingHiddenServiceOverPublicTorNetwork.java | 350 ++--
...AdvertisingHiddenServiceToPublicTorNetwork.java | 272 ++--
.../lspi/puppetor/impl/ClientApplicationImpl.java | 966 +++++-----
.../wiai/lspi/puppetor/impl/DirectoryNodeImpl.java | 914 ++++----
.../wiai/lspi/puppetor/impl/EventManagerImpl.java | 1510 +++++++-------
.../uniba/wiai/lspi/puppetor/impl/NetworkImpl.java | 2216 ++++++++++----------
.../wiai/lspi/puppetor/impl/ProxyNodeImpl.java | 1520 +++++++-------
.../wiai/lspi/puppetor/impl/RouterNodeImpl.java | 856 ++++----
.../lspi/puppetor/impl/ServerApplicationImpl.java | 754 ++++----
25 files changed, 7027 insertions(+), 7027 deletions(-)
mode change 100755 => 100644 doc/howto.tex
mode change 100755 => 100644 doc/logging
mode change 100755 => 100644 logging.properties
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/Event.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/EventListener.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/EventManager.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/EventType.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/Network.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/NodeState.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/RouterNode.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
mode change 100755 => 100644 src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
mode change 100755 => 100644 tools/create_keystores.sh
diff --git a/LICENSE b/LICENSE
index 00bebc8..77abd8d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,111 +1,111 @@
-===============================================================================
-PuppeTor - A Java-based Tor Simulator - is distributed under this license:
-
-Copyright (c) 2007, Karsten Loesing
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
- * Neither the names of the copyright owners nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-===============================================================================
-The Tor controller demonstration code is distributed under this license:
-
-Copyright (c) 2005, Nick Mathewson, Roger Dingledine
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
- * Neither the names of the copyright owners nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-===============================================================================
-Groovy is distributed under this license:
-
-Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
-
-Redistribution and use of this software and associated documentation
-("Software"), with or without modification, are permitted provided
-that the following conditions are met:
-
-1. Redistributions of source code must retain copyright
- statements and notices. Redistributions must also contain a
- copy of this document.
-
-2. Redistributions in binary form must reproduce the
- above copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
-
-3. The name "groovy" must not be used to endorse or promote
- products derived from this Software without prior written
- permission of The Codehaus. For written permission,
- please contact info@xxxxxxxxxxxxx
-
-4. Products derived from this Software may not be called "groovy"
- nor may "groovy" appear in their names without prior written
- permission of The Codehaus. "groovy" is a registered
- trademark of The Codehaus.
-
-5. Due credit should be given to The Codehaus -
- http://groovy.codehaus.org/
-
-THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
-NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+PuppeTor - A Java-based Tor Simulator - is distributed under this license:
+
+Copyright (c) 2007, Karsten Loesing
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============================================================================
+The Tor controller demonstration code is distributed under this license:
+
+Copyright (c) 2005, Nick Mathewson, Roger Dingledine
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============================================================================
+Groovy is distributed under this license:
+
+Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@xxxxxxxxxxxxx
+
+4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README b/README
index d55a3d5..bfa5a04 100644
--- a/README
+++ b/README
@@ -1,8 +1,8 @@
-This is a Java framework that facilitates the configuration of a set of local
-Tor processes and the execution of automatic tests based on these processes. The
-intention is to make it easier for developers to analyze Tor's behavior in
-arbitrary network settings and to measure the effects of changes to the Tor
-source code. Due to the automation of configuration and execution, these
-experiments can be done in an unsupervised batch fashion.
-
-For more information, read the how-to document in doc/howto.pdf .
+This is a Java framework that facilitates the configuration of a set of local
+Tor processes and the execution of automatic tests based on these processes. The
+intention is to make it easier for developers to analyze Tor's behavior in
+arbitrary network settings and to measure the effects of changes to the Tor
+source code. Due to the automation of configuration and execution, these
+experiments can be done in an unsupervised batch fashion.
+
+For more information, read the how-to document in doc/howto.pdf .
diff --git a/doc/howto.tex b/doc/howto.tex
old mode 100755
new mode 100644
diff --git a/doc/logging b/doc/logging
old mode 100755
new mode 100644
diff --git a/logging.properties b/logging.properties
old mode 100755
new mode 100644
index ae61850..52d399c
--- a/logging.properties
+++ b/logging.properties
@@ -1,4 +1,4 @@
-handlers= java.util.logging.ConsoleHandler
-java.util.logging.ConsoleHandler.level = FINEST
-java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
-.level= FINE
+handlers= java.util.logging.ConsoleHandler
+java.util.logging.ConsoleHandler.level = FINEST
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+.level= FINE
diff --git a/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java b/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
old mode 100755
new mode 100644
index 97caf68..093fc99
--- a/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
+++ b/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
@@ -1,126 +1,126 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>ClientApplication</code> can be used to simulate simple
- * <code>HTTP GET</code> requests by a virtual local client. Therefore, an
- * address and a port are given to which the client shall connect. Requests are
- * performed by a background thread, so that multiple requests could be
- * performed at the same time.
- *
- * @author kloesing
- */
-public interface ClientApplication {
-
- /**
- * <p>
- * Performs one or more HTTP requests to a previously provided address and
- * port. All requests are performed by a thread in the background, so that
- * this method returns immediately. That thread will try for
- * <code>retries</code> times to make the request with a timeout of
- * <code>timeoutForEachRetry</code> milliseconds each. If an attempt is
- * not successful, the thread nevertheless waits for the timeout to expire
- * before performing the next attempt. If <code>stopOnSuccess</code> is
- * set to <code>true</code>, the thread will quit performing requests
- * immediately after the first successful request.
- * </p>
- *
- * <p>
- * For each sent request the application fires a
- * <event>ClientEventType.CLIENT_SENDING_REQUEST</code> event. On receiving
- * a reply it fires an event of type <code>ClientEventType.CLIENT_REPLY_RECEIVED</code>,
- * if a request is not successful or times out, a <code>ClientEventType.CLIENT_GAVE_UP_REQUEST</code>
- * event is fired. After all requests have been performed (either
- * successfully, or not) an event of type <code>ClientEventType.CLIENT_REQUESTS_PERFORMED</code>
- * is fired.
- * </p>
- *
- * TODO may this method only be invoked once?!
- *
- * @param retries
- * The number of retries that this client will perform. Must be
- * <code>1</code> or greater.
- * @param timeoutForEachRetry
- * The timeout for each request. If a request is not successful,
- * the thread nevertheless waits for the timeout to expire. Must
- * not be negative.
- * @param stopOnSuccess
- * If set to <code>true</code>, the client quits performing
- * requests after the first successful request, if <code>false</code>
- * it continues until all <code>retries</code> have been
- * processed.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract void startRequests(int retries, long timeoutForEachRetry,
- boolean stopOnSuccess);
-
- /**
- * Stops all requests that are currently running.
- *
- * @throws IllegalStateException
- * Thrown if no requests have been started before.
- */
- public abstract void stopRequest();
-
- /**
- * Returns the name of this client.
- *
- * @return The name of this client.
- */
- public abstract String getClientApplicationName();
-
- /**
- * Returns the SOCKS port of the local Tor node to which requests are sent.
- *
- * @return The SOCKS port of the local Tor node to which requests are sent.
- */
- public abstract int getSocksPort();
-
- /**
- * Returns the target name for the requests sent by this client; can be
- * either a server name/address or an onion address.
- *
- * @return The target name for the requests sent by this client.
- */
- public abstract String getTargetName();
-
- /**
- * Returns the target port for the requests sent by this client; can be
- * either a server port or a virtual port of a hidden service.
- *
- * @return The target port for the requests sent by this client.
- */
- public abstract int getTargetPort();
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ClientApplication</code> can be used to simulate simple
+ * <code>HTTP GET</code> requests by a virtual local client. Therefore, an
+ * address and a port are given to which the client shall connect. Requests are
+ * performed by a background thread, so that multiple requests could be
+ * performed at the same time.
+ *
+ * @author kloesing
+ */
+public interface ClientApplication {
+
+ /**
+ * <p>
+ * Performs one or more HTTP requests to a previously provided address and
+ * port. All requests are performed by a thread in the background, so that
+ * this method returns immediately. That thread will try for
+ * <code>retries</code> times to make the request with a timeout of
+ * <code>timeoutForEachRetry</code> milliseconds each. If an attempt is
+ * not successful, the thread nevertheless waits for the timeout to expire
+ * before performing the next attempt. If <code>stopOnSuccess</code> is
+ * set to <code>true</code>, the thread will quit performing requests
+ * immediately after the first successful request.
+ * </p>
+ *
+ * <p>
+ * For each sent request the application fires a
+ * <event>ClientEventType.CLIENT_SENDING_REQUEST</code> event. On receiving
+ * a reply it fires an event of type <code>ClientEventType.CLIENT_REPLY_RECEIVED</code>,
+ * if a request is not successful or times out, a <code>ClientEventType.CLIENT_GAVE_UP_REQUEST</code>
+ * event is fired. After all requests have been performed (either
+ * successfully, or not) an event of type <code>ClientEventType.CLIENT_REQUESTS_PERFORMED</code>
+ * is fired.
+ * </p>
+ *
+ * TODO may this method only be invoked once?!
+ *
+ * @param retries
+ * The number of retries that this client will perform. Must be
+ * <code>1</code> or greater.
+ * @param timeoutForEachRetry
+ * The timeout for each request. If a request is not successful,
+ * the thread nevertheless waits for the timeout to expire. Must
+ * not be negative.
+ * @param stopOnSuccess
+ * If set to <code>true</code>, the client quits performing
+ * requests after the first successful request, if <code>false</code>
+ * it continues until all <code>retries</code> have been
+ * processed.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract void startRequests(int retries, long timeoutForEachRetry,
+ boolean stopOnSuccess);
+
+ /**
+ * Stops all requests that are currently running.
+ *
+ * @throws IllegalStateException
+ * Thrown if no requests have been started before.
+ */
+ public abstract void stopRequest();
+
+ /**
+ * Returns the name of this client.
+ *
+ * @return The name of this client.
+ */
+ public abstract String getClientApplicationName();
+
+ /**
+ * Returns the SOCKS port of the local Tor node to which requests are sent.
+ *
+ * @return The SOCKS port of the local Tor node to which requests are sent.
+ */
+ public abstract int getSocksPort();
+
+ /**
+ * Returns the target name for the requests sent by this client; can be
+ * either a server name/address or an onion address.
+ *
+ * @return The target name for the requests sent by this client.
+ */
+ public abstract String getTargetName();
+
+ /**
+ * Returns the target port for the requests sent by this client; can be
+ * either a server port or a virtual port of a hidden service.
+ *
+ * @return The target port for the requests sent by this client.
+ */
+ public abstract int getTargetPort();
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java b/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
old mode 100755
new mode 100644
index 4a6f739..7bb94cf
--- a/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
+++ b/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
@@ -1,75 +1,75 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.util.Set;
-
-/**
- * A <code>DirectoryNode</code> represents a Tor process that acts as
- * <code>RouterNode</code> and which is further a directory authoritative
- * server for the (private) Tor network. It inherits most of the configuration
- * and behavior from <code>RouterNode</code> and adds some directory-specific
- * configurations and behavior.
- *
- * @author kloesing
- */
-public interface DirectoryNode extends RouterNode {
-
- /**
- * Combines the fingerprint of this node to a <code>DirServer</code>
- * string that can be used to configure this or other nodes to use this node
- * as directory server.
- *
- * @return <code>DirServer</code> string to configure a node to use this
- * node as directory server.
- * @throws PuppeTorException
- * Thrown if a problem occurs when determining the fingerprint
- * of this node.
- */
- public abstract String getDirServerString() throws PuppeTorException;
-
- /**
- * Adds the given (possibly empty) set of onion router fingerprints to the
- * set of approved routers to confirm to directory clients, that the given
- * routers can be trusted. Changes are only stored locally and not written
- * to the <code>approved-routers</code> file to disk which will be done
- * when writing the configuration of this node.
- *
- * @param approvedRouters
- * The set of approved routers to be added. Each provided string
- * must be formatted as
- * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed as parameter.
- */
- public void addApprovedRouters(Set<String> approvedRouters);
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.Set;
+
+/**
+ * A <code>DirectoryNode</code> represents a Tor process that acts as
+ * <code>RouterNode</code> and which is further a directory authoritative
+ * server for the (private) Tor network. It inherits most of the configuration
+ * and behavior from <code>RouterNode</code> and adds some directory-specific
+ * configurations and behavior.
+ *
+ * @author kloesing
+ */
+public interface DirectoryNode extends RouterNode {
+
+ /**
+ * Combines the fingerprint of this node to a <code>DirServer</code>
+ * string that can be used to configure this or other nodes to use this node
+ * as directory server.
+ *
+ * @return <code>DirServer</code> string to configure a node to use this
+ * node as directory server.
+ * @throws PuppeTorException
+ * Thrown if a problem occurs when determining the fingerprint
+ * of this node.
+ */
+ public abstract String getDirServerString() throws PuppeTorException;
+
+ /**
+ * Adds the given (possibly empty) set of onion router fingerprints to the
+ * set of approved routers to confirm to directory clients, that the given
+ * routers can be trusted. Changes are only stored locally and not written
+ * to the <code>approved-routers</code> file to disk which will be done
+ * when writing the configuration of this node.
+ *
+ * @param approvedRouters
+ * The set of approved routers to be added. Each provided string
+ * must be formatted as
+ * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ */
+ public void addApprovedRouters(Set<String> approvedRouters);
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/Event.java b/src/de/uniba/wiai/lspi/puppetor/Event.java
old mode 100755
new mode 100644
diff --git a/src/de/uniba/wiai/lspi/puppetor/EventListener.java b/src/de/uniba/wiai/lspi/puppetor/EventListener.java
old mode 100755
new mode 100644
diff --git a/src/de/uniba/wiai/lspi/puppetor/EventManager.java b/src/de/uniba/wiai/lspi/puppetor/EventManager.java
old mode 100755
new mode 100644
index 5ae74fe..ebbac42
--- a/src/de/uniba/wiai/lspi/puppetor/EventManager.java
+++ b/src/de/uniba/wiai/lspi/puppetor/EventManager.java
@@ -1,257 +1,257 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.util.List;
-
-/**
- * The <code>EventManager</code> is the central place for a test run to manage
- * asynchronous events by Tor processes and client or server applications
- * running as threads in the background. A test application can either register
- * event listeners to be notified asynchronously about events when they occur,
- * or synchronize with an event by being blocked until a certain event occurs.
- *
- * @author kloesing
- */
-public interface EventManager {
-
- /**
- * Registers the given <code>listener</code> as event listener for events
- * originating from the given <code>source</code>. This method returns a
- * list of all previously fired events by this source, so that each event
- * fired by this source is either included in the returned list or
- * signalized in a later invocation on the event listener, but not in both.
- * This prevents race conditions by eliminating the gap between registration
- * of an event handler and asking if an event has been fired before
- * registering.
- *
- * @param source
- * The name of the source of events that the listener is
- * interested in. May not be <code>null</code> and must be the
- * name of a previously created node, client, or server.
- * @param listener
- * The listener that wants to be notified about events from the
- * given <code>source</code>. If the <code>listener</code>
- * is already registered for the same <code>source</code>,
- * nothing happens, i.e. the <code>listener</code> will not
- * receive multiple invocations for the same event. May not be
- * <code>null</code>.
- * @return A list of all previously fired events for the given
- * <code>source</code>. If no event has been fired before, an
- * empty list is returned instead of <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed for either of the
- * parameters or if the <code>source</code> is unknown.
- */
- public abstract List<Event> addEventListener(String source,
- EventListener listener);
-
- /**
- * Registers the given <code>listener</code> as event listener for future
- * events originating from any source.
- *
- * @param listener
- * The listener that wants to be notified about events from the
- * given <code>source</code>. If the <code>listener</code>
- * is already registered for all sources, nothing happens, i.e.
- * the <code>listener</code> will not receive multiple
- * invocations for the same event. May not be <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed for the parameter.
- */
- public abstract void addEventListener(EventListener listener);
-
- /**
- * Returns the list of all previously observed events from the given
- * <code>source</code>.
- *
- * @param source
- * The source of the events that the invoking thread is
- * interested in. May not be <code>null</code> and must be the
- * name of a previously created node, client, or server.
- * @return List of all previously observed events from the given
- * <code>source</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed as parameter or if
- * the <code>source</code> is unknown.
- */
- public abstract List<Event> getEventHistory(String source);
-
- /**
- * Returns whether the given <code>eventType</code> has been observed from
- * the given <code>source</code> before, or not.
- *
- * @param source
- * The source of the event that the invoking thread is interested
- * in. May not be <code>null</code> and must be the name of a
- * previously created node, client, or server.
- * @param eventType
- * The event type that the invoking thread is interested in. May
- * not be <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed for either of the
- * parameters or if <code>source</code> is unknown.
- * @return <code>true</code> if the event has been observed from the
- * source before, <code>false</code> otherwise.
- */
- public abstract boolean hasEventOccured(String source, EventType eventType);
-
- /**
- * Removes the given <code>listener</code> as event listener from all
- * previously registered sources. If this listener is not registered for any
- * source, nothing happens.
- *
- * @param listener
- * The listener that shall be removed from the list of registered
- * listeners. May not be <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed as parameter.
- */
- public abstract void removeEventListener(EventListener listener);
-
- /**
- * Checks if the given <code>eventType</code> has been observed from the
- * given <code>source</code> before; if not, blocks the invoking thread
- * until the next event of this type is fired from that source. Note that
- * this method does not restrict waiting to a timeout, so that it could
- * potentially block forever!
- *
- * @param source
- * The source of the event that the invoking thread is willing to
- * wait for. May not be <code>null</code> and must be the name
- * of a previously created node, client, or server.
- * @param eventType
- * The event type that the invoking thread is willing to wait for
- * from the given <code>source</code>. May not be
- * <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed for either of the
- * parameters or if the <code>source</code> is unknown.
- */
- public abstract void waitForAnyOccurence(String source, EventType eventType);
-
- /**
- * Checks if the given <code>eventType</code> has been observed from the
- * given <code>source</code> before; if not, blocks the invoking thread
- * until the next event of this type is fired from that source or the given
- * timeout of <code>maximumTimeToWaitInMillis</code> milliseconds has
- * expired.
- *
- * @param source
- * The source of the event that the invoking thread is willing to
- * wait for. May not be <code>null</code> and must be the name
- * of a previously created node, client, or server.
- * @param eventType
- * The event type that the invoking thread is willing to wait for
- * from the given <code>source</code>. May not be
- * <code>null</code>.
- * @param maximumTimeToWaitInMillis
- * The maximum time to wait in milliseconds. A positive value or
- * zero restricts waiting to this time. If this value is
- * negative, we will wait potentially forever.
- * @return <code>true</code> if an event of the given type has been fired
- * by the <code>source</code> within the given timeout,
- * <code>false</code> otherwise.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is passed for either of the
- * parameters or if the <code>source</code> is unknown.
- */
- public abstract boolean waitForAnyOccurence(String source,
- EventType eventType, long maximumTimeToWaitInMillis);
-
- /**
- * Blocks the invoking thread until the next <code>event</code> is fired
- * from the given <code>source</code>. This method only waits for the
- * next occurence of an event, regardless of previous occurrences. Note that
- * this method does not restrict waiting to a timeout, so that it could
- * potentially block forever!
- *
- * @param source
- * The source of the event that the invoking thread is willing to
- * wait for. May not be <code>null</code> and must be the name
- * of a previously created node, client, or server.
- * @param eventType
- * The event type that the invoking thread is willing to wait for
- * from the given <code>source</code>. May not be
- * <code>null</code>.
- * @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed for either of the
- * parameters or if the <code>source</code> is unknown.
- */
- public abstract void waitForNextOccurence(String source, EventType eventType);
-
- /**
- * Blocks the invoking thread until the next <code>event</code> is fired
- * from the given <code>source</code> or the given timeout of
- * <code>maximumTimeToWaitInMillis</code> milliseconds has expired. This
- * method only waits for the next occurence of an event, regardless of
- * previous occurrences.
- *
- * @param source
- * The source of the event that the invoking thread is willing to
- * wait for. May not be <code>null</code> and must be the name
- * of a previously created node, client, or server.
- * @param eventType
- * The event type that the invoking thread is willing to wait for
- * from the given <code>source</code>. May not be
- * <code>null</code>.
- * @param maximumTimeToWaitInMillis
- * The maximum time to wait in milliseconds. A positive value or
- * zero restricts waiting to this time. If this value is
- * negative, we will wait potentially forever.
- * @return <code>true</code> if an event of the given type has been fired
- * by the <code>source</code> within the given timeout,
- * <code>false</code> otherwise.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is passed for either of the
- * parameters or if the <code>source</code> is unknown.
- */
- public abstract boolean waitForNextOccurence(String source,
- EventType eventType, long maximumTimeToWaitInMillis);
-
- /**
- * Registers a new event type by passing a pattern string that can be
- * applied to a regular expression when parsing Tor log statements. This is
- * useful for log statements that are only included in modified Tor
- * versions. Therefore, the event type may be an instance of a self-defined
- * class that implements <code>EventType</code>.
- *
- * @param patternString
- * The pattern string that will be used for parsing Tor log
- * statements; the syntax corresponds to java.util.regex.Pattern.
- * @param eventType
- * The event type of the event that will be fired when a log
- * statement was parsed that includes the given pattern.
- */
- public abstract void registerEventTypePattern(String patternString,
- EventType eventType);
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.List;
+
+/**
+ * The <code>EventManager</code> is the central place for a test run to manage
+ * asynchronous events by Tor processes and client or server applications
+ * running as threads in the background. A test application can either register
+ * event listeners to be notified asynchronously about events when they occur,
+ * or synchronize with an event by being blocked until a certain event occurs.
+ *
+ * @author kloesing
+ */
+public interface EventManager {
+
+ /**
+ * Registers the given <code>listener</code> as event listener for events
+ * originating from the given <code>source</code>. This method returns a
+ * list of all previously fired events by this source, so that each event
+ * fired by this source is either included in the returned list or
+ * signalized in a later invocation on the event listener, but not in both.
+ * This prevents race conditions by eliminating the gap between registration
+ * of an event handler and asking if an event has been fired before
+ * registering.
+ *
+ * @param source
+ * The name of the source of events that the listener is
+ * interested in. May not be <code>null</code> and must be the
+ * name of a previously created node, client, or server.
+ * @param listener
+ * The listener that wants to be notified about events from the
+ * given <code>source</code>. If the <code>listener</code>
+ * is already registered for the same <code>source</code>,
+ * nothing happens, i.e. the <code>listener</code> will not
+ * receive multiple invocations for the same event. May not be
+ * <code>null</code>.
+ * @return A list of all previously fired events for the given
+ * <code>source</code>. If no event has been fired before, an
+ * empty list is returned instead of <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters or if the <code>source</code> is unknown.
+ */
+ public abstract List<Event> addEventListener(String source,
+ EventListener listener);
+
+ /**
+ * Registers the given <code>listener</code> as event listener for future
+ * events originating from any source.
+ *
+ * @param listener
+ * The listener that wants to be notified about events from the
+ * given <code>source</code>. If the <code>listener</code>
+ * is already registered for all sources, nothing happens, i.e.
+ * the <code>listener</code> will not receive multiple
+ * invocations for the same event. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for the parameter.
+ */
+ public abstract void addEventListener(EventListener listener);
+
+ /**
+ * Returns the list of all previously observed events from the given
+ * <code>source</code>.
+ *
+ * @param source
+ * The source of the events that the invoking thread is
+ * interested in. May not be <code>null</code> and must be the
+ * name of a previously created node, client, or server.
+ * @return List of all previously observed events from the given
+ * <code>source</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter or if
+ * the <code>source</code> is unknown.
+ */
+ public abstract List<Event> getEventHistory(String source);
+
+ /**
+ * Returns whether the given <code>eventType</code> has been observed from
+ * the given <code>source</code> before, or not.
+ *
+ * @param source
+ * The source of the event that the invoking thread is interested
+ * in. May not be <code>null</code> and must be the name of a
+ * previously created node, client, or server.
+ * @param eventType
+ * The event type that the invoking thread is interested in. May
+ * not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters or if <code>source</code> is unknown.
+ * @return <code>true</code> if the event has been observed from the
+ * source before, <code>false</code> otherwise.
+ */
+ public abstract boolean hasEventOccured(String source, EventType eventType);
+
+ /**
+ * Removes the given <code>listener</code> as event listener from all
+ * previously registered sources. If this listener is not registered for any
+ * source, nothing happens.
+ *
+ * @param listener
+ * The listener that shall be removed from the list of registered
+ * listeners. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ */
+ public abstract void removeEventListener(EventListener listener);
+
+ /**
+ * Checks if the given <code>eventType</code> has been observed from the
+ * given <code>source</code> before; if not, blocks the invoking thread
+ * until the next event of this type is fired from that source. Note that
+ * this method does not restrict waiting to a timeout, so that it could
+ * potentially block forever!
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code> and must be the name
+ * of a previously created node, client, or server.
+ * @param eventType
+ * The event type that the invoking thread is willing to wait for
+ * from the given <code>source</code>. May not be
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters or if the <code>source</code> is unknown.
+ */
+ public abstract void waitForAnyOccurence(String source, EventType eventType);
+
+ /**
+ * Checks if the given <code>eventType</code> has been observed from the
+ * given <code>source</code> before; if not, blocks the invoking thread
+ * until the next event of this type is fired from that source or the given
+ * timeout of <code>maximumTimeToWaitInMillis</code> milliseconds has
+ * expired.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code> and must be the name
+ * of a previously created node, client, or server.
+ * @param eventType
+ * The event type that the invoking thread is willing to wait for
+ * from the given <code>source</code>. May not be
+ * <code>null</code>.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. If this value is
+ * negative, we will wait potentially forever.
+ * @return <code>true</code> if an event of the given type has been fired
+ * by the <code>source</code> within the given timeout,
+ * <code>false</code> otherwise.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is passed for either of the
+ * parameters or if the <code>source</code> is unknown.
+ */
+ public abstract boolean waitForAnyOccurence(String source,
+ EventType eventType, long maximumTimeToWaitInMillis);
+
+ /**
+ * Blocks the invoking thread until the next <code>event</code> is fired
+ * from the given <code>source</code>. This method only waits for the
+ * next occurence of an event, regardless of previous occurrences. Note that
+ * this method does not restrict waiting to a timeout, so that it could
+ * potentially block forever!
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code> and must be the name
+ * of a previously created node, client, or server.
+ * @param eventType
+ * The event type that the invoking thread is willing to wait for
+ * from the given <code>source</code>. May not be
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters or if the <code>source</code> is unknown.
+ */
+ public abstract void waitForNextOccurence(String source, EventType eventType);
+
+ /**
+ * Blocks the invoking thread until the next <code>event</code> is fired
+ * from the given <code>source</code> or the given timeout of
+ * <code>maximumTimeToWaitInMillis</code> milliseconds has expired. This
+ * method only waits for the next occurence of an event, regardless of
+ * previous occurrences.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code> and must be the name
+ * of a previously created node, client, or server.
+ * @param eventType
+ * The event type that the invoking thread is willing to wait for
+ * from the given <code>source</code>. May not be
+ * <code>null</code>.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. If this value is
+ * negative, we will wait potentially forever.
+ * @return <code>true</code> if an event of the given type has been fired
+ * by the <code>source</code> within the given timeout,
+ * <code>false</code> otherwise.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is passed for either of the
+ * parameters or if the <code>source</code> is unknown.
+ */
+ public abstract boolean waitForNextOccurence(String source,
+ EventType eventType, long maximumTimeToWaitInMillis);
+
+ /**
+ * Registers a new event type by passing a pattern string that can be
+ * applied to a regular expression when parsing Tor log statements. This is
+ * useful for log statements that are only included in modified Tor
+ * versions. Therefore, the event type may be an instance of a self-defined
+ * class that implements <code>EventType</code>.
+ *
+ * @param patternString
+ * The pattern string that will be used for parsing Tor log
+ * statements; the syntax corresponds to java.util.regex.Pattern.
+ * @param eventType
+ * The event type of the event that will be fired when a log
+ * statement was parsed that includes the given pattern.
+ */
+ public abstract void registerEventTypePattern(String patternString,
+ EventType eventType);
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/EventType.java b/src/de/uniba/wiai/lspi/puppetor/EventType.java
old mode 100755
new mode 100644
index c1d8b15..13836f3
--- a/src/de/uniba/wiai/lspi/puppetor/EventType.java
+++ b/src/de/uniba/wiai/lspi/puppetor/EventType.java
@@ -1,52 +1,52 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.io.Serializable;
-
-/**
- * The super interface of possible event types that are fired on a state change
- * of an asynchronous system component, e.g. a Tor process or a client/server
- * application running as thread in the background.
- *
- * @author kloesing
- */
-public interface EventType extends Serializable {
-
- /**
- * Returns a string representation of the event type name for display
- * purposes.
- *
- * @return String representation of the event type name.
- */
- public abstract String getTypeName();
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.io.Serializable;
+
+/**
+ * The super interface of possible event types that are fired on a state change
+ * of an asynchronous system component, e.g. a Tor process or a client/server
+ * application running as thread in the background.
+ *
+ * @author kloesing
+ */
+public interface EventType extends Serializable {
+
+ /**
+ * Returns a string representation of the event type name for display
+ * purposes.
+ *
+ * @return String representation of the event type name.
+ */
+ public abstract String getTypeName();
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/Network.java b/src/de/uniba/wiai/lspi/puppetor/Network.java
old mode 100755
new mode 100644
index 1219b4d..f2569f9
--- a/src/de/uniba/wiai/lspi/puppetor/Network.java
+++ b/src/de/uniba/wiai/lspi/puppetor/Network.java
@@ -1,683 +1,683 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Network instance constitutes the central object of any test run and is
- * aware of the node configuration. It creates all nodes for this configuration
- * and is able to perform common operations on these nodes. Apart from the
- * factory methods, all other operations could also be performed manually by an
- * application using the appropriate interfaces.
- *
- * @author kloesing
- */
-public interface Network {
-
- /**
- * <p>
- * Configures this network as private Tor network by exchanging directory
- * strings and router fingerprints between the nodes of this network.
- * Afterwards, the nodes will be able to run a private Tor network,
- * separated from public directory servers and onion routers.
- * </p>
- *
- * <p>
- * The configuration is done in two steps:
- * <ol>
- * <li>Directory strings of directory nodes are added to the configurations
- * of all nodes in the other network.</li>
- * <li>Router fingerprints of all router and directory nodes are added to
- * the <code>approved-routers</code> files of the directory nodes.</li>
- * </ol>
- * </p>
- *
- * <p>
- * This operation may be invoked in any state of the contained nodes.
- * However, a network that does not have directory nodes of its own but
- * relies on directory nodes of a merged network <b>should not be started
- * before being configured as private network!</b> Otherwise it would
- * connect to the public Tor network before being merged with the other
- * private Tor network. However, it may also be invoked at a later time,
- * e.g. to admit new nodes.
- * </p>
- *
- * <p>
- * This operation does not write any configurations to disk and neither
- * starts a nodes nor sends HUP signals to running nodes. These operations
- * are left to the application, so that they have more control over the
- * network behavior.
- * </p>
- *
- * <p>
- * Applications need to ensure that there are enough directory nodes (2) and
- * router nodes (3) in the network to allow normal operation.
- * </p>
- *
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while determining the nodes'
- * fingerprints.
- */
- public abstract void configureAsPrivateNetwork() throws PuppeTorException;
-
- /**
- * XXX document properly -SH
- */
- public void configureAsPartOfPrivateNetwork(
- List<String> authorizedDirectoriesFingerprints);
-
-
- /**
- * Creates a new client application, but does not yet perform a request.
- *
- * @param clientApplicationName
- * The name for this client application, which is used for
- * logging purposes and as event source. May neither be
- * <code>null</code> or a zero-length string. The name needs to
- * be unique in this network.
- * @param targetAddress
- * The target for requests sent by this client application. Can
- * be a publicly available URL or an onion address. May neither
- * be <code>null</code> or a zero-length string.
- * @param targetPort
- * The TCP port for requests sent by this client application. If
- * the target address is an onion address, this port is the
- * virtual port that the hidden service has announced. May not be
- * negative or greater than 65535.
- * @param socksPort
- * The TCP port on which a local Tor process is waiting for
- * incoming SOCKS requests. May not be negative or greater than
- * 65535.
- * @return Reference to the created client application.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract ClientApplication createClient(
- String clientApplicationName, String targetAddress, int targetPort,
- int socksPort);
-
- /**
- * Creates a new directory node with automatically assigned ports and adds
- * it to the network, but does not yet write its configuration to disk or
- * start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @return Reference to the created directory node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as node name.
- */
- public abstract DirectoryNode createDirectory(String nodeName);
-
- /**
- * Creates a new directory node with automatically assigned ports that will
- * listen on the given IP address and adds it to the network, but does not
- * yet write its configuration to disk or start the corresponding Tor
- * process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @return Reference to the created directory node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as node name.
- */
- public abstract DirectoryNode createDirectory(String nodeName,
- String serverIpAddress);
-
- /**
- * Creates a new directory node and adds it to the network, but does not yet
- * write its configuration to disk or start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param controlPort
- * The TCP port on which the corresponding Tor process will wait
- * for a controller. May not be negative or greater than 65535.
- * @param socksPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming SOCKS requests. May not be negative or greater
- * than 65535.
- * @param orPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming requests from other onion routers. May not be
- * negative or greater than 65535.
- * @param dirPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming directory requests. May not be negative or
- * greater than 65535.
- * @return Reference to the created directory node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract DirectoryNode createDirectory(String nodeName,
- int controlPort, int socksPort, int orPort, int dirPort);
-
- /**
- * Creates a new directory node that will listen on the given IP address and
- * adds it to the network, but does not yet write its configuration to disk
- * or start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param controlPort
- * The TCP port on which the corresponding Tor process will wait
- * for a controller. May not be negative or greater than 65535.
- * @param socksPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming SOCKS requests. May not be negative or greater
- * than 65535.
- * @param orPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming requests from other onion routers. May not be
- * negative or greater than 65535.
- * @param dirPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming directory requests. May not be negative or
- * greater than 65535.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @return Reference to the created directory node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract DirectoryNode createDirectory(String nodeName,
- int controlPort, int socksPort, int orPort, int dirPort,
- String serverIpAddress);
-
- /**
- * Creates a new <code>ProxyNode</code> with automatically assigned ports
- * and adds it to the network, but does not yet write its configuration to
- * disk or start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, and as event source. May
- * neither be <code>null</code> or have zero or more than 19
- * alpha-numeric characters. The node name needs to be unique in
- * this network.
- * @return Reference to the created proxy node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as node name.
- */
- public abstract ProxyNode createProxy(String nodeName);
-
- /**
- * Creates a new <code>ProxyNode</code> and adds it to the network, but
- * does not yet write its configuration to disk or start the corresponding
- * Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, and as event source. May
- * neither be <code>null</code> or have zero or more than 19
- * alpha-numeric characters. The node name needs to be unique in
- * this network.
- * @param controlPort
- * The TCP port on which the corresponding Tor process will wait
- * for a controller. May not be negative or greater than 65535.
- * @param socksPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming SOCKS requests. May not be negative or greater
- * than 65535.
- * @return Reference to the created proxy node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract ProxyNode createProxy(String nodeName, int controlPort,
- int socksPort);
-
- /**
- * Creates a new <code>RouterNode</code> with automatically assigned ports
- * and adds it to the network, but does not yet write its configuration to
- * disk or start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @return Reference to the created router node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as node name.
- */
- public abstract RouterNode createRouter(String nodeName);
-
- /**
- * Creates a new <code>RouterNode</code> and adds it to the network, but
- * does not yet write its configuration to disk or start the corresponding
- * Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param controlPort
- * The TCP port on which the corresponding Tor process will wait
- * for a controller. May not be negative or greater than 65535.
- * @param socksPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming SOCKS requests. May not be negative or greater
- * than 65535.
- * @param orPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming requests from other onion routers. May not be
- * negative or greater than 65535.
- * @param dirPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming directory requests which in fact are requests for
- * the mirrored directory. May not be negative or greater than
- * 65535.
- * @return Reference to the created router node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract RouterNode createRouter(String nodeName, int controlPort,
- int socksPort, int orPort, int dirPort);
-
- /**
- * Creates a new <code>RouterNode</code> with automatically assigned ports
- * that will listen on the given IP address and adds it to the network, but
- * does not yet write its configuration to disk or start the corresponding
- * Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @return Reference to the created router node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as node name.
- */
- public abstract RouterNode createRouter(String nodeName,
- String serverIpAddress);
-
- /**
- * Creates a new <code>RouterNode</code> that will listen on the given IP
- * address and adds it to the network, but does not yet write its
- * configuration to disk or start the corresponding Tor process.
- *
- * @param nodeName
- * The name for this node, which is used as name for the working
- * directory, for logging purposes, as node nickname, and as
- * event source. May neither be <code>null</code> or have zero
- * or more than 19 alpha-numeric characters. The node name needs
- * to be unique in this network.
- * @param controlPort
- * The TCP port on which the corresponding Tor process will wait
- * for a controller. May not be negative or greater than 65535.
- * @param socksPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming SOCKS requests. May not be negative or greater
- * than 65535.
- * @param orPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming requests from other onion routers. May not be
- * negative or greater than 65535.
- * @param dirPort
- * The TCP port on which the corresponding Tor process will wait
- * for incoming directory requests which in fact are requests for
- * the mirrored directory. May not be negative or greater than
- * 65535.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @return Reference to the created router node.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract RouterNode createRouter(String nodeName, int controlPort,
- int socksPort, int orPort, int dirPort, String serverIpAddress);
-
- /**
- * Creates a new <code>ServerApplication</code> with automatically
- * assigned ports, but does not start listening for incoming requests.
- *
- * @param serverApplicationName
- * The name for this server application, which is used for
- * logging purposes and as event source. May neither be
- * <code>null</code> or a zero-length string. The name needs to
- * be unique in this network.
- * @return Reference to the created server application.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given as server application
- * name.
- */
- public abstract ServerApplication createServer(String serverApplicationName);
-
- /**
- * Creates a new <code>ServerApplication</code>, but does not start
- * listening for incoming requests.
- *
- * @param serverApplicationName
- * The name for this server application, which is used for
- * logging purposes and as event source. May neither be
- * <code>null</code> or a zero-length string. The name needs to
- * be unique in this network.
- * @param serverPort
- * The TCP port on which the server will wait for incoming
- * requests. May not be negative or greater than 65535.
- * @return Reference to the created server application.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract ServerApplication createServer(
- String serverApplicationName, int serverPort);
-
- /**
- * Returns a reference on the (single) event manager for this network.
- *
- * @return Reference on the (single) event manager for this network.
- */
- public abstract EventManager getEventManager();
-
- /**
- * Returns (a copy of) the map containing the names of all directory nodes
- * as keys and the corresponding directory nodes as values.
- *
- * @return Map containing all directory nodes.
- */
- public abstract Map<String, DirectoryNode> getAllDirectoryNodes();
-
- /**
- * Returns (a copy of) the map containing the names of all router nodes
- * (only those that are not acting as directory nodes at the same time) as
- * keys and the corresponding router nodes as values.
- *
- * @return Map containing all router nodes.
- */
- public abstract Map<String, RouterNode> getAllRouterNodes();
-
- /**
- * Returns (a copy of) the map containing the names of all proxy nodes (only
- * those that are not acting as router or directory nodes at the same time)
- * as keys and the corresponding proxy nodes as values.
- *
- * @return Map containing all proxy nodes.
- */
- public abstract Map<String, ProxyNode> getAllProxyNodes();
-
- /**
- * Returns (a copy of) the map containing the names of all nodes as keys and
- * the corresponding proxy nodes as values.
- *
- * @return Map containing all nodes.
- */
- public abstract Map<String, ProxyNode> getAllNodes();
-
- /**
- * Returns the node with name <code>nodeName</code> or <code>null</code>
- * if no such node exists.
- *
- * @param nodeName
- * The node name to look up.
- * @return The node with name <code>nodeName</code>.
- */
- public abstract ProxyNode getNode(String nodeName);
-
- /**
- * <p>
- * Sends a HUP signal to all nodes in the network in regular intervals and
- * blocks the invoking thread until all nodes have reported to have
- * successfully opened a circuit.
- * </p>
- *
- * <p>
- * First, the method waits for <code>hupInterval</code> milliseconds for
- * the nodes to have successfully opened a circuit. If they do not succeed
- * within this time, a HUP signal is sent to all nodes and the method waits
- * for another <code>hupInterval</code> milliseconds. In total, the method
- * sends at most <code>tries</code> HUP signals before giving up and
- * returning with <code>false</code>. Thus, the maximum waiting time is
- * <code>(tries + 1)</code> times <code>hupInterval</code>. As soon as
- * all nodes have successfully opened circuits, the method returns with
- * <code>true</code>. This operation can only be invoked, if all nodes in
- * the network are in state <code>NodeState.RUNNING</code>.
- * </p>
- *
- * @param tries
- * The maximum number of HUP signals that are sent to the Tor
- * processes. Negative values are not allowed. A value of zero
- * means to wait only for the given time of
- * <code>hupInterval</code> milliseconds without sending a HUP
- * signal. Typical values depend on the network being a public or
- * private Tor network and range about 3 to 5 tries.
- * @param hupInterval
- * The time in milliseconds that the method will wait between
- * sending HUP signals. Negative values are not allowed.
- * Typically, values should not be smaller than 5 seconds to
- * permit Tor to stabilize.
- * @throws IllegalStateException
- * Thrown if at least one node is not in state
- * <code>NodeState.RUNNING</code>.
- * @throws IllegalArgumentException
- * Thrown if a negative value is given for either
- * <code>tries</code> or <code>hupInterval</code>.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while sending HUP signals.
- * @return <code>true</code> if all nodes have reported to have
- * successfully opened a circuit, <code>false</code> otherwise.
- */
- public abstract boolean hupUntilUp(int tries, long hupInterval)
- throws PuppeTorException;
-
- /**
- * Sends a HUP signal to all nodes in the network once. This operation can
- * only be invoked, if all nodes in the network are in state
- * <code>NodeState.RUNNING</code>.
- *
- * @throws IllegalStateException
- * Thrown if at least one node is not in state
- * <code>NodeState.RUNNING</code>.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while sending HUP signals.
- */
- public abstract void hupAllNodes() throws PuppeTorException;
-
- /**
- * Sends a HUP signal to all directory nodes in the network once. This
- * operation can only be invoked, if all directory nodes in the network are
- * in state <code>NodeState.RUNNING</code>.
- *
- * @throws IllegalStateException
- * Thrown if at least one directory node is not in state
- * <code>NodeState.RUNNING</code>.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while sending HUP signals.
- */
- public abstract void hupAllDirectories() throws PuppeTorException;
-
- /**
- * Attempts to shut down all running nodes. The method blocks until all
- * shutdown requests have been sent and either returns, or throws the first
- * exception that has been observed when shutting down nodes. The method can
- * be assumed to return very quickly. If there are no running nodes in this
- * network, this operation has no effect.
- *
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while shutting down the
- * nodes.
- */
- public abstract void shutdownNodes() throws PuppeTorException;
-
- /**
- * Attempts to start all nodes within a given timeout of
- * <code>maximumTimeToWaitInMillis</code> milliseconds. The method returns
- * as soon as all nodes have started and opened their control port so that
- * we can connect to them. It returns a boolean that states whether the
- * operation was either successful or has timed out. This operation can only
- * be invoked, if all nodes in the network have written their configuration,
- * i.e. are not in state <code>NodeState.CONFIGURING</code> anymore.
- *
- * @param maximumTimeToWaitInMillis
- * The maximum time to wait in milliseconds. A positive value or
- * zero restricts waiting to this time. Negative values are not
- * allowed. Typical values are in the range of a few seconds.
- * @return <code>true</code> if all nodes could be started successfully,
- * <code>false</code> if a timeout has occured.
- * @throws IllegalStateException
- * Thrown if at least one node in the network is still in state
- * <code>NodeState.CONFIGURING</code>.
- * @throws IllegalArgumentException
- * Thrown if a negative value is given for
- * <code>maximumTimeToWaitInMillis</code>.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while starting the nodes.
- */
- public abstract boolean startNodes(long maximumTimeToWaitInMillis)
- throws PuppeTorException;
-
- /**
- * Writes the configurations for all nodes in the network to disk, including
- * <code>torrc</code> and <code>approved-routers</code> files. This
- * method is assumed to return very quickly. In case of a private network,
- * <code>configureAsPrivateNetwork</code> should be invoked in advance to
- * this method!
- *
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while writing to the nodes'
- * working directories.
- */
- public abstract void writeConfigurations() throws PuppeTorException;
-
- /**
- * Returns the working directory of this network configuration which is in
- * <code>test-env/networkName/</code>.
- *
- * @return Working directory of this network.
- */
- public abstract File getWorkingDirectory();
-
- /**
- * Returns all configuration strings of the template of a node class that
- * will be added to future instances of this node class and its subclasses.
- * Note that the result only contains those configuration strings that are
- * added by this node class to possible superclasses and that parameterized
- * configuration strings, e.g. port configurations, are not included.
- *
- * @param nodeClass
- * The class which will be configured with the returned template
- * configuration; may not be <code>null</code>.
- * @return The template configuration for the given node class.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for the parameter.
- */
- public abstract List<String> getTemplateConfiguration(
- Class<? extends ProxyNode> nodeClass);
-
- /**
- * Adds a configuration string to the template of a node class, so that it
- * will be added to future instances of this node class and its subclasses.
- *
- * @param nodeClass
- * The class of nodes of which future instances will have the
- * given configuration string; may not be <code>null</code>.
- * @param templateConfigurationString
- * The configuration string to add; may neither be
- * <code>null</code> nor a zero-length string, and must consist
- * of configuration key and value.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract void addTemplateConfiguration(
- Class<? extends ProxyNode> nodeClass,
- String templateConfigurationString);
-
- /**
- * Removes a configuration string from the template of a node class, so that
- * it will not be added to future instances of this node class and its
- * subclasses.
- *
- * @param nodeClass
- * The class of nodes of which future instances will have the
- * given configuration string; may not be <code>null</code>.
- * @param templateConfigurationKey
- * The configuration key to remove; may neither be
- * <code>null</code> nor a zero-length key.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract void removeTemplateConfiguration(
- Class<? extends ProxyNode> nodeClass,
- String templateConfigurationKey);
-
- /**
- * Returns the name of this network.
- *
- * @return The name of this network.
- */
- public abstract String getNetworkName();
-
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Network instance constitutes the central object of any test run and is
+ * aware of the node configuration. It creates all nodes for this configuration
+ * and is able to perform common operations on these nodes. Apart from the
+ * factory methods, all other operations could also be performed manually by an
+ * application using the appropriate interfaces.
+ *
+ * @author kloesing
+ */
+public interface Network {
+
+ /**
+ * <p>
+ * Configures this network as private Tor network by exchanging directory
+ * strings and router fingerprints between the nodes of this network.
+ * Afterwards, the nodes will be able to run a private Tor network,
+ * separated from public directory servers and onion routers.
+ * </p>
+ *
+ * <p>
+ * The configuration is done in two steps:
+ * <ol>
+ * <li>Directory strings of directory nodes are added to the configurations
+ * of all nodes in the other network.</li>
+ * <li>Router fingerprints of all router and directory nodes are added to
+ * the <code>approved-routers</code> files of the directory nodes.</li>
+ * </ol>
+ * </p>
+ *
+ * <p>
+ * This operation may be invoked in any state of the contained nodes.
+ * However, a network that does not have directory nodes of its own but
+ * relies on directory nodes of a merged network <b>should not be started
+ * before being configured as private network!</b> Otherwise it would
+ * connect to the public Tor network before being merged with the other
+ * private Tor network. However, it may also be invoked at a later time,
+ * e.g. to admit new nodes.
+ * </p>
+ *
+ * <p>
+ * This operation does not write any configurations to disk and neither
+ * starts a nodes nor sends HUP signals to running nodes. These operations
+ * are left to the application, so that they have more control over the
+ * network behavior.
+ * </p>
+ *
+ * <p>
+ * Applications need to ensure that there are enough directory nodes (2) and
+ * router nodes (3) in the network to allow normal operation.
+ * </p>
+ *
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while determining the nodes'
+ * fingerprints.
+ */
+ public abstract void configureAsPrivateNetwork() throws PuppeTorException;
+
+ /**
+ * XXX document properly -SH
+ */
+ public void configureAsPartOfPrivateNetwork(
+ List<String> authorizedDirectoriesFingerprints);
+
+
+ /**
+ * Creates a new client application, but does not yet perform a request.
+ *
+ * @param clientApplicationName
+ * The name for this client application, which is used for
+ * logging purposes and as event source. May neither be
+ * <code>null</code> or a zero-length string. The name needs to
+ * be unique in this network.
+ * @param targetAddress
+ * The target for requests sent by this client application. Can
+ * be a publicly available URL or an onion address. May neither
+ * be <code>null</code> or a zero-length string.
+ * @param targetPort
+ * The TCP port for requests sent by this client application. If
+ * the target address is an onion address, this port is the
+ * virtual port that the hidden service has announced. May not be
+ * negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which a local Tor process is waiting for
+ * incoming SOCKS requests. May not be negative or greater than
+ * 65535.
+ * @return Reference to the created client application.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ClientApplication createClient(
+ String clientApplicationName, String targetAddress, int targetPort,
+ int socksPort);
+
+ /**
+ * Creates a new directory node with automatically assigned ports and adds
+ * it to the network, but does not yet write its configuration to disk or
+ * start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @return Reference to the created directory node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as node name.
+ */
+ public abstract DirectoryNode createDirectory(String nodeName);
+
+ /**
+ * Creates a new directory node with automatically assigned ports that will
+ * listen on the given IP address and adds it to the network, but does not
+ * yet write its configuration to disk or start the corresponding Tor
+ * process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @return Reference to the created directory node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as node name.
+ */
+ public abstract DirectoryNode createDirectory(String nodeName,
+ String serverIpAddress);
+
+ /**
+ * Creates a new directory node and adds it to the network, but does not yet
+ * write its configuration to disk or start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests. May not be negative or
+ * greater than 65535.
+ * @return Reference to the created directory node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract DirectoryNode createDirectory(String nodeName,
+ int controlPort, int socksPort, int orPort, int dirPort);
+
+ /**
+ * Creates a new directory node that will listen on the given IP address and
+ * adds it to the network, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests. May not be negative or
+ * greater than 65535.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @return Reference to the created directory node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract DirectoryNode createDirectory(String nodeName,
+ int controlPort, int socksPort, int orPort, int dirPort,
+ String serverIpAddress);
+
+ /**
+ * Creates a new <code>ProxyNode</code> with automatically assigned ports
+ * and adds it to the network, but does not yet write its configuration to
+ * disk or start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, and as event source. May
+ * neither be <code>null</code> or have zero or more than 19
+ * alpha-numeric characters. The node name needs to be unique in
+ * this network.
+ * @return Reference to the created proxy node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as node name.
+ */
+ public abstract ProxyNode createProxy(String nodeName);
+
+ /**
+ * Creates a new <code>ProxyNode</code> and adds it to the network, but
+ * does not yet write its configuration to disk or start the corresponding
+ * Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, and as event source. May
+ * neither be <code>null</code> or have zero or more than 19
+ * alpha-numeric characters. The node name needs to be unique in
+ * this network.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @return Reference to the created proxy node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ProxyNode createProxy(String nodeName, int controlPort,
+ int socksPort);
+
+ /**
+ * Creates a new <code>RouterNode</code> with automatically assigned ports
+ * and adds it to the network, but does not yet write its configuration to
+ * disk or start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @return Reference to the created router node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as node name.
+ */
+ public abstract RouterNode createRouter(String nodeName);
+
+ /**
+ * Creates a new <code>RouterNode</code> and adds it to the network, but
+ * does not yet write its configuration to disk or start the corresponding
+ * Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests which in fact are requests for
+ * the mirrored directory. May not be negative or greater than
+ * 65535.
+ * @return Reference to the created router node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract RouterNode createRouter(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort);
+
+ /**
+ * Creates a new <code>RouterNode</code> with automatically assigned ports
+ * that will listen on the given IP address and adds it to the network, but
+ * does not yet write its configuration to disk or start the corresponding
+ * Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @return Reference to the created router node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as node name.
+ */
+ public abstract RouterNode createRouter(String nodeName,
+ String serverIpAddress);
+
+ /**
+ * Creates a new <code>RouterNode</code> that will listen on the given IP
+ * address and adds it to the network, but does not yet write its
+ * configuration to disk or start the corresponding Tor process.
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, as node nickname, and as
+ * event source. May neither be <code>null</code> or have zero
+ * or more than 19 alpha-numeric characters. The node name needs
+ * to be unique in this network.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests which in fact are requests for
+ * the mirrored directory. May not be negative or greater than
+ * 65535.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @return Reference to the created router node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract RouterNode createRouter(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort, String serverIpAddress);
+
+ /**
+ * Creates a new <code>ServerApplication</code> with automatically
+ * assigned ports, but does not start listening for incoming requests.
+ *
+ * @param serverApplicationName
+ * The name for this server application, which is used for
+ * logging purposes and as event source. May neither be
+ * <code>null</code> or a zero-length string. The name needs to
+ * be unique in this network.
+ * @return Reference to the created server application.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given as server application
+ * name.
+ */
+ public abstract ServerApplication createServer(String serverApplicationName);
+
+ /**
+ * Creates a new <code>ServerApplication</code>, but does not start
+ * listening for incoming requests.
+ *
+ * @param serverApplicationName
+ * The name for this server application, which is used for
+ * logging purposes and as event source. May neither be
+ * <code>null</code> or a zero-length string. The name needs to
+ * be unique in this network.
+ * @param serverPort
+ * The TCP port on which the server will wait for incoming
+ * requests. May not be negative or greater than 65535.
+ * @return Reference to the created server application.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ServerApplication createServer(
+ String serverApplicationName, int serverPort);
+
+ /**
+ * Returns a reference on the (single) event manager for this network.
+ *
+ * @return Reference on the (single) event manager for this network.
+ */
+ public abstract EventManager getEventManager();
+
+ /**
+ * Returns (a copy of) the map containing the names of all directory nodes
+ * as keys and the corresponding directory nodes as values.
+ *
+ * @return Map containing all directory nodes.
+ */
+ public abstract Map<String, DirectoryNode> getAllDirectoryNodes();
+
+ /**
+ * Returns (a copy of) the map containing the names of all router nodes
+ * (only those that are not acting as directory nodes at the same time) as
+ * keys and the corresponding router nodes as values.
+ *
+ * @return Map containing all router nodes.
+ */
+ public abstract Map<String, RouterNode> getAllRouterNodes();
+
+ /**
+ * Returns (a copy of) the map containing the names of all proxy nodes (only
+ * those that are not acting as router or directory nodes at the same time)
+ * as keys and the corresponding proxy nodes as values.
+ *
+ * @return Map containing all proxy nodes.
+ */
+ public abstract Map<String, ProxyNode> getAllProxyNodes();
+
+ /**
+ * Returns (a copy of) the map containing the names of all nodes as keys and
+ * the corresponding proxy nodes as values.
+ *
+ * @return Map containing all nodes.
+ */
+ public abstract Map<String, ProxyNode> getAllNodes();
+
+ /**
+ * Returns the node with name <code>nodeName</code> or <code>null</code>
+ * if no such node exists.
+ *
+ * @param nodeName
+ * The node name to look up.
+ * @return The node with name <code>nodeName</code>.
+ */
+ public abstract ProxyNode getNode(String nodeName);
+
+ /**
+ * <p>
+ * Sends a HUP signal to all nodes in the network in regular intervals and
+ * blocks the invoking thread until all nodes have reported to have
+ * successfully opened a circuit.
+ * </p>
+ *
+ * <p>
+ * First, the method waits for <code>hupInterval</code> milliseconds for
+ * the nodes to have successfully opened a circuit. If they do not succeed
+ * within this time, a HUP signal is sent to all nodes and the method waits
+ * for another <code>hupInterval</code> milliseconds. In total, the method
+ * sends at most <code>tries</code> HUP signals before giving up and
+ * returning with <code>false</code>. Thus, the maximum waiting time is
+ * <code>(tries + 1)</code> times <code>hupInterval</code>. As soon as
+ * all nodes have successfully opened circuits, the method returns with
+ * <code>true</code>. This operation can only be invoked, if all nodes in
+ * the network are in state <code>NodeState.RUNNING</code>.
+ * </p>
+ *
+ * @param tries
+ * The maximum number of HUP signals that are sent to the Tor
+ * processes. Negative values are not allowed. A value of zero
+ * means to wait only for the given time of
+ * <code>hupInterval</code> milliseconds without sending a HUP
+ * signal. Typical values depend on the network being a public or
+ * private Tor network and range about 3 to 5 tries.
+ * @param hupInterval
+ * The time in milliseconds that the method will wait between
+ * sending HUP signals. Negative values are not allowed.
+ * Typically, values should not be smaller than 5 seconds to
+ * permit Tor to stabilize.
+ * @throws IllegalStateException
+ * Thrown if at least one node is not in state
+ * <code>NodeState.RUNNING</code>.
+ * @throws IllegalArgumentException
+ * Thrown if a negative value is given for either
+ * <code>tries</code> or <code>hupInterval</code>.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while sending HUP signals.
+ * @return <code>true</code> if all nodes have reported to have
+ * successfully opened a circuit, <code>false</code> otherwise.
+ */
+ public abstract boolean hupUntilUp(int tries, long hupInterval)
+ throws PuppeTorException;
+
+ /**
+ * Sends a HUP signal to all nodes in the network once. This operation can
+ * only be invoked, if all nodes in the network are in state
+ * <code>NodeState.RUNNING</code>.
+ *
+ * @throws IllegalStateException
+ * Thrown if at least one node is not in state
+ * <code>NodeState.RUNNING</code>.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while sending HUP signals.
+ */
+ public abstract void hupAllNodes() throws PuppeTorException;
+
+ /**
+ * Sends a HUP signal to all directory nodes in the network once. This
+ * operation can only be invoked, if all directory nodes in the network are
+ * in state <code>NodeState.RUNNING</code>.
+ *
+ * @throws IllegalStateException
+ * Thrown if at least one directory node is not in state
+ * <code>NodeState.RUNNING</code>.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while sending HUP signals.
+ */
+ public abstract void hupAllDirectories() throws PuppeTorException;
+
+ /**
+ * Attempts to shut down all running nodes. The method blocks until all
+ * shutdown requests have been sent and either returns, or throws the first
+ * exception that has been observed when shutting down nodes. The method can
+ * be assumed to return very quickly. If there are no running nodes in this
+ * network, this operation has no effect.
+ *
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while shutting down the
+ * nodes.
+ */
+ public abstract void shutdownNodes() throws PuppeTorException;
+
+ /**
+ * Attempts to start all nodes within a given timeout of
+ * <code>maximumTimeToWaitInMillis</code> milliseconds. The method returns
+ * as soon as all nodes have started and opened their control port so that
+ * we can connect to them. It returns a boolean that states whether the
+ * operation was either successful or has timed out. This operation can only
+ * be invoked, if all nodes in the network have written their configuration,
+ * i.e. are not in state <code>NodeState.CONFIGURING</code> anymore.
+ *
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. Negative values are not
+ * allowed. Typical values are in the range of a few seconds.
+ * @return <code>true</code> if all nodes could be started successfully,
+ * <code>false</code> if a timeout has occured.
+ * @throws IllegalStateException
+ * Thrown if at least one node in the network is still in state
+ * <code>NodeState.CONFIGURING</code>.
+ * @throws IllegalArgumentException
+ * Thrown if a negative value is given for
+ * <code>maximumTimeToWaitInMillis</code>.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while starting the nodes.
+ */
+ public abstract boolean startNodes(long maximumTimeToWaitInMillis)
+ throws PuppeTorException;
+
+ /**
+ * Writes the configurations for all nodes in the network to disk, including
+ * <code>torrc</code> and <code>approved-routers</code> files. This
+ * method is assumed to return very quickly. In case of a private network,
+ * <code>configureAsPrivateNetwork</code> should be invoked in advance to
+ * this method!
+ *
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while writing to the nodes'
+ * working directories.
+ */
+ public abstract void writeConfigurations() throws PuppeTorException;
+
+ /**
+ * Returns the working directory of this network configuration which is in
+ * <code>test-env/networkName/</code>.
+ *
+ * @return Working directory of this network.
+ */
+ public abstract File getWorkingDirectory();
+
+ /**
+ * Returns all configuration strings of the template of a node class that
+ * will be added to future instances of this node class and its subclasses.
+ * Note that the result only contains those configuration strings that are
+ * added by this node class to possible superclasses and that parameterized
+ * configuration strings, e.g. port configurations, are not included.
+ *
+ * @param nodeClass
+ * The class which will be configured with the returned template
+ * configuration; may not be <code>null</code>.
+ * @return The template configuration for the given node class.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for the parameter.
+ */
+ public abstract List<String> getTemplateConfiguration(
+ Class<? extends ProxyNode> nodeClass);
+
+ /**
+ * Adds a configuration string to the template of a node class, so that it
+ * will be added to future instances of this node class and its subclasses.
+ *
+ * @param nodeClass
+ * The class of nodes of which future instances will have the
+ * given configuration string; may not be <code>null</code>.
+ * @param templateConfigurationString
+ * The configuration string to add; may neither be
+ * <code>null</code> nor a zero-length string, and must consist
+ * of configuration key and value.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract void addTemplateConfiguration(
+ Class<? extends ProxyNode> nodeClass,
+ String templateConfigurationString);
+
+ /**
+ * Removes a configuration string from the template of a node class, so that
+ * it will not be added to future instances of this node class and its
+ * subclasses.
+ *
+ * @param nodeClass
+ * The class of nodes of which future instances will have the
+ * given configuration string; may not be <code>null</code>.
+ * @param templateConfigurationKey
+ * The configuration key to remove; may neither be
+ * <code>null</code> nor a zero-length key.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract void removeTemplateConfiguration(
+ Class<? extends ProxyNode> nodeClass,
+ String templateConfigurationKey);
+
+ /**
+ * Returns the name of this network.
+ *
+ * @return The name of this network.
+ */
+ public abstract String getNetworkName();
+
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java b/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
old mode 100755
new mode 100644
index 7d8abda..35fe2d9
--- a/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
+++ b/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
@@ -1,108 +1,108 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import de.uniba.wiai.lspi.puppetor.impl.NetworkImpl;
-
-/**
- * The <code>NetworkFactory</code> is a concrete factory that can create
- * <code>Network</code> instances.
- *
- * TODO At the moment, this class uses the concrete class NetworkImpl to
- * implement its only factory method. If we want to make this a real abstract
- * factory, we need to replace the concrete constructor by reading the class
- * name of the class implementing Network from a property file and invoking its
- * constructor using reflection. Currently, this is the only place where we
- * reference a class from the impl package.
- *
- * @author kloesing
- */
-public abstract class NetworkFactory {
-
- final private static ConcurrentMap<String, Network> networks =
- new ConcurrentHashMap<String, Network>();
-
- /**
- * Creates a new network that is required for a test run. The new network is
- * initially unpopulated and creates its own working directory at
- * test-env/randomTestID/. The network automatically assigns port numbers to
- * newly created nodes starting at <code>7000</code>.
- *
- * @param networkName
- * Name of this network configuration.
- * @return A new network instance.
- */
- public static Network createNetwork(final String networkName) {
- final Network network = new NetworkImpl(networkName);
- networks.put(networkName, network);
- return network;
- }
-
- /**
- * Creates a new network that is required for a test run. The new network is
- * initially unpopulated and creates its own working directory at
- * test-env/randomTestID/. The network automatically assigns port numbers to
- * newly created nodes starting at <code>startPort</code>.
- *
- * @param networkName
- * Name of this network configuration.
- * @param startPort
- * The initial value for automatically assigned port numbers of
- * nodes created by this <code>Network</code>; must be a value
- * between <code>1024</code> and <code>65535</code>.
- * Applications need to ensure that there are enough ports left
- * to the maximum number port <code>65535</code> for all
- * created nodes.
- * @return A new network instance.
- */
- public static Network createNetwork(final String networkName,
- final int startPort) {
- final Network network = new NetworkImpl(networkName, startPort);
- networks.put(networkName, network);
- return network;
- }
-
- /**
- *
- */
- public static Network getNetworkByName(final String networkName) {
- final Network network = networks.get(networkName);
- if (network == null) {
- throw new IllegalStateException("BUG: The network with name "
- + networkName + " must exist and be non-null!");
- }
- return network;
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import de.uniba.wiai.lspi.puppetor.impl.NetworkImpl;
+
+/**
+ * The <code>NetworkFactory</code> is a concrete factory that can create
+ * <code>Network</code> instances.
+ *
+ * TODO At the moment, this class uses the concrete class NetworkImpl to
+ * implement its only factory method. If we want to make this a real abstract
+ * factory, we need to replace the concrete constructor by reading the class
+ * name of the class implementing Network from a property file and invoking its
+ * constructor using reflection. Currently, this is the only place where we
+ * reference a class from the impl package.
+ *
+ * @author kloesing
+ */
+public abstract class NetworkFactory {
+
+ final private static ConcurrentMap<String, Network> networks =
+ new ConcurrentHashMap<String, Network>();
+
+ /**
+ * Creates a new network that is required for a test run. The new network is
+ * initially unpopulated and creates its own working directory at
+ * test-env/randomTestID/. The network automatically assigns port numbers to
+ * newly created nodes starting at <code>7000</code>.
+ *
+ * @param networkName
+ * Name of this network configuration.
+ * @return A new network instance.
+ */
+ public static Network createNetwork(final String networkName) {
+ final Network network = new NetworkImpl(networkName);
+ networks.put(networkName, network);
+ return network;
+ }
+
+ /**
+ * Creates a new network that is required for a test run. The new network is
+ * initially unpopulated and creates its own working directory at
+ * test-env/randomTestID/. The network automatically assigns port numbers to
+ * newly created nodes starting at <code>startPort</code>.
+ *
+ * @param networkName
+ * Name of this network configuration.
+ * @param startPort
+ * The initial value for automatically assigned port numbers of
+ * nodes created by this <code>Network</code>; must be a value
+ * between <code>1024</code> and <code>65535</code>.
+ * Applications need to ensure that there are enough ports left
+ * to the maximum number port <code>65535</code> for all
+ * created nodes.
+ * @return A new network instance.
+ */
+ public static Network createNetwork(final String networkName,
+ final int startPort) {
+ final Network network = new NetworkImpl(networkName, startPort);
+ networks.put(networkName, network);
+ return network;
+ }
+
+ /**
+ *
+ */
+ public static Network getNetworkByName(final String networkName) {
+ final Network network = networks.get(networkName);
+ if (network == null) {
+ throw new IllegalStateException("BUG: The network with name "
+ + networkName + " must exist and be non-null!");
+ }
+ return network;
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/NodeState.java b/src/de/uniba/wiai/lspi/puppetor/NodeState.java
old mode 100755
new mode 100644
index 3c5ca43..2d06c93
--- a/src/de/uniba/wiai/lspi/puppetor/NodeState.java
+++ b/src/de/uniba/wiai/lspi/puppetor/NodeState.java
@@ -1,70 +1,70 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>NodeState</code> constitutes the state of a single Tor node. In
- * contrast to <code>EventType</code> the node states depend only on the
- * methods that have been invoked on these objects, and not on asynchronous
- * state changes. Most operations of <code>ProxyNode</code> and its subclasses
- * require a certain <code>NodeState</code> as precondition and may ensure
- * another <code>NodeState</code> as postcondition. There is a prescribed
- * order of states.
- *
- * @author kloesing
- */
-public enum NodeState {
-
- /**
- * The configuration of this node has not been written to disk. This is the
- * initial state of a <code>ProxyNode</code> or one of its subclasses.
- */
- CONFIGURING,
-
- /**
- * The configuration of this node has been written to disk, but the Tor
- * process has not been started, yet. This state could be useful to review
- * the configuration that has been written to disk.
- */
- CONFIGURATION_WRITTEN,
-
- /**
- * The node has been started and is running.
- */
- RUNNING,
-
- /**
- * The node had been started and shut down. It cannot be started at a later
- * time anymore.
- */
- SHUT_DOWN
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>NodeState</code> constitutes the state of a single Tor node. In
+ * contrast to <code>EventType</code> the node states depend only on the
+ * methods that have been invoked on these objects, and not on asynchronous
+ * state changes. Most operations of <code>ProxyNode</code> and its subclasses
+ * require a certain <code>NodeState</code> as precondition and may ensure
+ * another <code>NodeState</code> as postcondition. There is a prescribed
+ * order of states.
+ *
+ * @author kloesing
+ */
+public enum NodeState {
+
+ /**
+ * The configuration of this node has not been written to disk. This is the
+ * initial state of a <code>ProxyNode</code> or one of its subclasses.
+ */
+ CONFIGURING,
+
+ /**
+ * The configuration of this node has been written to disk, but the Tor
+ * process has not been started, yet. This state could be useful to review
+ * the configuration that has been written to disk.
+ */
+ CONFIGURATION_WRITTEN,
+
+ /**
+ * The node has been started and is running.
+ */
+ RUNNING,
+
+ /**
+ * The node had been started and shut down. It cannot be started at a later
+ * time anymore.
+ */
+ SHUT_DOWN
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java b/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
old mode 100755
new mode 100644
index 9846603..490c646
--- a/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
+++ b/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
@@ -1,295 +1,295 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-import java.util.List;
-
-/**
- * <p>
- * A <code>ProxyNode</code> represents a Tor process that is configured as
- * onion proxy, i.e. to relay traffic from a local application to the Tor
- * network and vice versa, and does not route traffic on behalf of remote
- * applications. It is the superclass for other node types that extend the
- * configuration of a </code>ProxyNode</code>.
- * </p>
- *
- * <p>
- * <b>Pay extra attention when using in private network!</b> Using proxy nodes
- * in private networks in the same way as router nodes will fail! Tor has two
- * different strategies for downloading network status documents: Directory
- * caches (router nodes) download these documents after every HUP signal and
- * then accept all contained router entries. But directory clients (proxy nodes)
- * only download network status documents, if the most recent download lies at
- * least 30 minutes in the past, and then accept only those of the contained
- * router entries that are at least 10 minutes old. However, when starting all
- * nodes of a private network at once, directories cannot contain 10 minutes old
- * router descriptors. You have at least the following options to cope with this
- * problem:
- * </p>
- *
- * <ul>
- * <li>Use router nodes instead of proxy nodes,</li>
- * <li>start proxy nodes with a delay of at least 10 minutes to be sure that
- * the router descriptors stored at directory authorities will be accepted by
- * directory clients, or</li>
- * <li>change the constants <code>ESTIMATED_PROPAGATION_TIME</code> and
- * <code>NETWORKSTATUS_CLIENT_DL_INTERVAL</code> in Tor to values smaller than
- * your overall HUP time for starting the network.</li>
- * </ul>
- *
- * @author kloesing
- */
-public interface ProxyNode {
-
- /**
- * Adds the entries for a hidden service to the configuration of this node.
- *
- * @param serviceName
- * Name of the hidden service that will be used as name for the
- * hidden service directory. May neither be <code>null</code>
- * or a zero-length string.
- * @param servicePort
- * The TCP port on which the service will be available for
- * requests. This can, but need not be different from the virtual
- * port that is announced to clients. May not be negative or
- * greater than 65535.
- * @param virtualPort
- * The virtual TCP port that this hidden service runs on as it is
- * announced to clients. May not be negative or greater than
- * 65535.
- * @return <code>HiddenService</code> object containing the configuration
- * of the created hidden service.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract HiddenService addHiddenService(String serviceName,
- int servicePort, int virtualPort);
-
- /**
- * Adds the entries for a hidden service with virtual port 80 to the
- * configuration of this node.
- *
- * @param serviceName
- * Name of the hidden service that will be used as name for the
- * hidden service directory. May neither be <code>null</code>
- * or a zero-length string.
- * @param servicePort
- * The TCP port on which the service will be available for
- * requests. This can, but need not be different from the virtual
- * port that is announced to clients. May not be negative or
- * greater than 65535.
- * @return <code>HiddenService</code> object containing the configuration
- * of the created hidden service.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for either of the
- * parameters.
- */
- public abstract HiddenService addHiddenService(String serviceName,
- int servicePort);
-
- /**
- * Adds the entries for a hidden service with an automatically assigned
- * service port and virtual port 80 to the configuration of this node.
- *
- * service port automatically assigned virtual port 80
- *
- * @param serviceName
- * Name of the hidden service that will be used as name for the
- * hidden service directory. May neither be <code>null</code>
- * or a zero-length string.
- * @return <code>HiddenService</code> object containing the configuration
- * of the created hidden service.
- * @throws IllegalArgumentException
- * Thrown if an invalid value is given for the parameter.
- */
- public abstract HiddenService addHiddenService(String serviceName);
-
- /**
- * Adds the given configuration string, consisting of "<configuration key>
- * <configuration value>", to the configuration of this node.
- *
- * @param configurationString
- * The configuration string to be added.
- * @throws IllegalArgumentException
- * Thrown if the given configuration string is either
- * <code>null</code>, a zero-length string, or does not
- * consist of configuration key and value.
- */
- public abstract void addConfiguration(String configurationString);
-
- /**
- * Adds the given configuration strings, each consisting of "<configuration
- * key> <configuration value>", to the configuration of this node.
- *
- * @param configurationStrings
- * A list of the configuration strings to be added.
- * @throws IllegalArgumentException
- * Thrown if the given list is <code>null</code>, or any of
- * the contained strings is either <code>null</code>, a
- * zero-length string, or does not consist of configuration key
- * and value.
- */
- public abstract void addConfigurations(List<String> configurationStrings);
-
- /**
- * Replaces the first configuration string, consisting of "<configuration
- * key> <configuration value>", that contains the same configuration key as
- * <code>configurationString</code> by this new configuration string; if
- * multiple occurrences of the given configuration key are found, only the
- * first occurrence is replaced; if no configuration can be found, the
- * configuration string is appended.
- *
- * @param configurationString
- * The replacing configuration string.
- * @throws IllegalArgumentException
- * Thrown if the given configuration string is either
- * <code>null</code>, a zero-length string, or does not
- * consist of configuration key and value.
- */
- public abstract void replaceConfiguration(String configurationString);
-
- /**
- * Removes all configuration strings containing the given configuration key
- * in "<configuration key> <configuration value>", regardless of their
- * configuration value.
- *
- * @param configurationKey
- * The configuration key to remove.
- * @throws IllegalArgumentException
- * Thrown if the given configuration key is either
- * <code>null</code> or a zero-length key.
- */
- public abstract void removeConfiguration(String configurationKey);
-
- /**
- * Returns the name of this node.
- *
- * @return The name of this node.
- */
- public abstract String getNodeName();
-
- /**
- * Returns the state of this node.
- *
- * @return The state of this node.
- */
- public abstract NodeState getNodeState();
-
- /**
- * Sends a HUP command to the process via its control port to restart it;
- * can only be done if the node has already been started, i.e. is in state
- * <code>NodeState.RUNNING</code>!
- *
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while sending the HUP signal.
- * @throws IllegalStateException
- * Thrown if node is not in state <code>NodeState.RUNNING</code>.
- */
- public abstract void hup() throws PuppeTorException;
-
- /**
- * Shuts down the Tor process corresponding to this node immediately. This
- * is done by sending the <code>SHUTDOWN</code> signal twice, so that
- * those nodes extending <code>ProxyNode</code> which have opened their OR
- * port shutdown immediately, too.
- *
- * @throws IllegalStateException
- * Thrown if this node is not in state
- * <code>NodeState.RUNNING</code>.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while sending the
- * <code>SHUTDOWN</code> signal.
- */
- public abstract void shutdown() throws PuppeTorException;
-
- /**
- * Starts the Tor process for this node and connects to the control port as
- * soon as it is opened. <b>In order for this method to succeed it is
- * absolutely necessary, that logging on the console is not changed in the
- * configuration of this node to a higher level than NOTICE, because the
- * output is parsed to see when the control port is opened.</b>
- *
- * @param maximumTimeToWaitInMillis
- * Maximum time in milliseconds we will wait for the Tor process
- * to be started and the control port being opened. If this value
- * is negative or zero, we will wait potentially forever.
- * @return <code>true</code> if the node could be started successfully,
- * <code>false</code> otherwise.
- * @throws IllegalStateException
- * Thrown if node is not in state
- * <code>NodeState.CONFIGURATION_WRITTEN</code>, i.e. if
- * either configuration has not been written or the process has
- * already been started.
- * @throws PuppeTorException
- * Thrown if either the process could not be started, or the
- * connection to the control port could not be established.
- */
- public abstract boolean startNode(long maximumTimeToWaitInMillis)
- throws PuppeTorException;
-
- /**
- * Writes the configuration of this node to the <code>torrc</code> file in
- * its working directory and changes the state to
- * <code>NodeState.CONFIGURATION_WRITTEN</code>, if it was in state
- * <code>NodeState.CONFIGURING</code> before.
- *
- * @throws PuppeTorException
- * Thrown if the configuration file <code>torrc</code> cannot
- * be written to disk.
- */
- public abstract void writeConfiguration() throws PuppeTorException;
-
- /**
- * Returns the SOCKS port of this node.
- *
- * @return The SOCKS port of this node.
- */
- public abstract int getSocksPort();
-
- /**
- * Returns the control port of this node.
- *
- * @return The control port of this node.
- */
- public abstract int getControlPort();
-
- /**
- * Returns (a copy of) the list of strings containing the configuration of
- * this node.
- *
- * @return (A copy of) the list of strings containing the configuration of
- * this node.
- */
- public abstract List<String> getConfiguration();
-
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.List;
+
+/**
+ * <p>
+ * A <code>ProxyNode</code> represents a Tor process that is configured as
+ * onion proxy, i.e. to relay traffic from a local application to the Tor
+ * network and vice versa, and does not route traffic on behalf of remote
+ * applications. It is the superclass for other node types that extend the
+ * configuration of a </code>ProxyNode</code>.
+ * </p>
+ *
+ * <p>
+ * <b>Pay extra attention when using in private network!</b> Using proxy nodes
+ * in private networks in the same way as router nodes will fail! Tor has two
+ * different strategies for downloading network status documents: Directory
+ * caches (router nodes) download these documents after every HUP signal and
+ * then accept all contained router entries. But directory clients (proxy nodes)
+ * only download network status documents, if the most recent download lies at
+ * least 30 minutes in the past, and then accept only those of the contained
+ * router entries that are at least 10 minutes old. However, when starting all
+ * nodes of a private network at once, directories cannot contain 10 minutes old
+ * router descriptors. You have at least the following options to cope with this
+ * problem:
+ * </p>
+ *
+ * <ul>
+ * <li>Use router nodes instead of proxy nodes,</li>
+ * <li>start proxy nodes with a delay of at least 10 minutes to be sure that
+ * the router descriptors stored at directory authorities will be accepted by
+ * directory clients, or</li>
+ * <li>change the constants <code>ESTIMATED_PROPAGATION_TIME</code> and
+ * <code>NETWORKSTATUS_CLIENT_DL_INTERVAL</code> in Tor to values smaller than
+ * your overall HUP time for starting the network.</li>
+ * </ul>
+ *
+ * @author kloesing
+ */
+public interface ProxyNode {
+
+ /**
+ * Adds the entries for a hidden service to the configuration of this node.
+ *
+ * @param serviceName
+ * Name of the hidden service that will be used as name for the
+ * hidden service directory. May neither be <code>null</code>
+ * or a zero-length string.
+ * @param servicePort
+ * The TCP port on which the service will be available for
+ * requests. This can, but need not be different from the virtual
+ * port that is announced to clients. May not be negative or
+ * greater than 65535.
+ * @param virtualPort
+ * The virtual TCP port that this hidden service runs on as it is
+ * announced to clients. May not be negative or greater than
+ * 65535.
+ * @return <code>HiddenService</code> object containing the configuration
+ * of the created hidden service.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract HiddenService addHiddenService(String serviceName,
+ int servicePort, int virtualPort);
+
+ /**
+ * Adds the entries for a hidden service with virtual port 80 to the
+ * configuration of this node.
+ *
+ * @param serviceName
+ * Name of the hidden service that will be used as name for the
+ * hidden service directory. May neither be <code>null</code>
+ * or a zero-length string.
+ * @param servicePort
+ * The TCP port on which the service will be available for
+ * requests. This can, but need not be different from the virtual
+ * port that is announced to clients. May not be negative or
+ * greater than 65535.
+ * @return <code>HiddenService</code> object containing the configuration
+ * of the created hidden service.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract HiddenService addHiddenService(String serviceName,
+ int servicePort);
+
+ /**
+ * Adds the entries for a hidden service with an automatically assigned
+ * service port and virtual port 80 to the configuration of this node.
+ *
+ * service port automatically assigned virtual port 80
+ *
+ * @param serviceName
+ * Name of the hidden service that will be used as name for the
+ * hidden service directory. May neither be <code>null</code>
+ * or a zero-length string.
+ * @return <code>HiddenService</code> object containing the configuration
+ * of the created hidden service.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for the parameter.
+ */
+ public abstract HiddenService addHiddenService(String serviceName);
+
+ /**
+ * Adds the given configuration string, consisting of "<configuration key>
+ * <configuration value>", to the configuration of this node.
+ *
+ * @param configurationString
+ * The configuration string to be added.
+ * @throws IllegalArgumentException
+ * Thrown if the given configuration string is either
+ * <code>null</code>, a zero-length string, or does not
+ * consist of configuration key and value.
+ */
+ public abstract void addConfiguration(String configurationString);
+
+ /**
+ * Adds the given configuration strings, each consisting of "<configuration
+ * key> <configuration value>", to the configuration of this node.
+ *
+ * @param configurationStrings
+ * A list of the configuration strings to be added.
+ * @throws IllegalArgumentException
+ * Thrown if the given list is <code>null</code>, or any of
+ * the contained strings is either <code>null</code>, a
+ * zero-length string, or does not consist of configuration key
+ * and value.
+ */
+ public abstract void addConfigurations(List<String> configurationStrings);
+
+ /**
+ * Replaces the first configuration string, consisting of "<configuration
+ * key> <configuration value>", that contains the same configuration key as
+ * <code>configurationString</code> by this new configuration string; if
+ * multiple occurrences of the given configuration key are found, only the
+ * first occurrence is replaced; if no configuration can be found, the
+ * configuration string is appended.
+ *
+ * @param configurationString
+ * The replacing configuration string.
+ * @throws IllegalArgumentException
+ * Thrown if the given configuration string is either
+ * <code>null</code>, a zero-length string, or does not
+ * consist of configuration key and value.
+ */
+ public abstract void replaceConfiguration(String configurationString);
+
+ /**
+ * Removes all configuration strings containing the given configuration key
+ * in "<configuration key> <configuration value>", regardless of their
+ * configuration value.
+ *
+ * @param configurationKey
+ * The configuration key to remove.
+ * @throws IllegalArgumentException
+ * Thrown if the given configuration key is either
+ * <code>null</code> or a zero-length key.
+ */
+ public abstract void removeConfiguration(String configurationKey);
+
+ /**
+ * Returns the name of this node.
+ *
+ * @return The name of this node.
+ */
+ public abstract String getNodeName();
+
+ /**
+ * Returns the state of this node.
+ *
+ * @return The state of this node.
+ */
+ public abstract NodeState getNodeState();
+
+ /**
+ * Sends a HUP command to the process via its control port to restart it;
+ * can only be done if the node has already been started, i.e. is in state
+ * <code>NodeState.RUNNING</code>!
+ *
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while sending the HUP signal.
+ * @throws IllegalStateException
+ * Thrown if node is not in state <code>NodeState.RUNNING</code>.
+ */
+ public abstract void hup() throws PuppeTorException;
+
+ /**
+ * Shuts down the Tor process corresponding to this node immediately. This
+ * is done by sending the <code>SHUTDOWN</code> signal twice, so that
+ * those nodes extending <code>ProxyNode</code> which have opened their OR
+ * port shutdown immediately, too.
+ *
+ * @throws IllegalStateException
+ * Thrown if this node is not in state
+ * <code>NodeState.RUNNING</code>.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while sending the
+ * <code>SHUTDOWN</code> signal.
+ */
+ public abstract void shutdown() throws PuppeTorException;
+
+ /**
+ * Starts the Tor process for this node and connects to the control port as
+ * soon as it is opened. <b>In order for this method to succeed it is
+ * absolutely necessary, that logging on the console is not changed in the
+ * configuration of this node to a higher level than NOTICE, because the
+ * output is parsed to see when the control port is opened.</b>
+ *
+ * @param maximumTimeToWaitInMillis
+ * Maximum time in milliseconds we will wait for the Tor process
+ * to be started and the control port being opened. If this value
+ * is negative or zero, we will wait potentially forever.
+ * @return <code>true</code> if the node could be started successfully,
+ * <code>false</code> otherwise.
+ * @throws IllegalStateException
+ * Thrown if node is not in state
+ * <code>NodeState.CONFIGURATION_WRITTEN</code>, i.e. if
+ * either configuration has not been written or the process has
+ * already been started.
+ * @throws PuppeTorException
+ * Thrown if either the process could not be started, or the
+ * connection to the control port could not be established.
+ */
+ public abstract boolean startNode(long maximumTimeToWaitInMillis)
+ throws PuppeTorException;
+
+ /**
+ * Writes the configuration of this node to the <code>torrc</code> file in
+ * its working directory and changes the state to
+ * <code>NodeState.CONFIGURATION_WRITTEN</code>, if it was in state
+ * <code>NodeState.CONFIGURING</code> before.
+ *
+ * @throws PuppeTorException
+ * Thrown if the configuration file <code>torrc</code> cannot
+ * be written to disk.
+ */
+ public abstract void writeConfiguration() throws PuppeTorException;
+
+ /**
+ * Returns the SOCKS port of this node.
+ *
+ * @return The SOCKS port of this node.
+ */
+ public abstract int getSocksPort();
+
+ /**
+ * Returns the control port of this node.
+ *
+ * @return The control port of this node.
+ */
+ public abstract int getControlPort();
+
+ /**
+ * Returns (a copy of) the list of strings containing the configuration of
+ * this node.
+ *
+ * @return (A copy of) the list of strings containing the configuration of
+ * this node.
+ */
+ public abstract List<String> getConfiguration();
+
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java b/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java
old mode 100755
new mode 100644
index 5a78d76..7f16e9e
--- a/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java
+++ b/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java
@@ -1,91 +1,91 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>PuppeTorException</code> comprises all kinds of checked
- * exceptions that occur when interacting with the JVM-external Tor processes or
- * with the local file system. Any occurence of this exception denotes either a
- * configuration problem that can only be solved outside of the JVM, or an
- * unexpected problem. In contrast to this, all kinds of programming errors of
- * an application using this API (invoking a method with wrong parameter values,
- * in wrong state, etc.) will instead cause appropriate runtime exceptions from
- * the Java API.
- *
- * @author kloesing
- */
-@SuppressWarnings("serial")
-public class PuppeTorException extends Exception {
-
- /**
- * Creates a <code>PuppeTorException</code> without detail message or
- * cause.
- */
- public PuppeTorException() {
- super();
- }
-
- /**
- * Creates a <code>PuppeTorException</code> with the given detail
- * <code>message</code> and <code>cause</code>.
- *
- * @param message
- * The detail message of this exception.
- * @param cause
- * The cause for this exception.
- */
- public PuppeTorException(final String message, final Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Creates a <code>PuppeTorException</code> with the given detail
- * <code>message</code>, but without a <code>cause</code>.
- *
- * @param message
- * The detail message of this exception.
- */
- public PuppeTorException(final String message) {
- super(message);
- }
-
- /**
- * Creates a <code>PuppeTorException</code> with the given
- * <code>cause</code>, but without a detail message.
- *
- * @param cause
- * The cause for this exception.
- */
- public PuppeTorException(final Throwable cause) {
- super(cause);
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>PuppeTorException</code> comprises all kinds of checked
+ * exceptions that occur when interacting with the JVM-external Tor processes or
+ * with the local file system. Any occurence of this exception denotes either a
+ * configuration problem that can only be solved outside of the JVM, or an
+ * unexpected problem. In contrast to this, all kinds of programming errors of
+ * an application using this API (invoking a method with wrong parameter values,
+ * in wrong state, etc.) will instead cause appropriate runtime exceptions from
+ * the Java API.
+ *
+ * @author kloesing
+ */
+@SuppressWarnings("serial")
+public class PuppeTorException extends Exception {
+
+ /**
+ * Creates a <code>PuppeTorException</code> without detail message or
+ * cause.
+ */
+ public PuppeTorException() {
+ super();
+ }
+
+ /**
+ * Creates a <code>PuppeTorException</code> with the given detail
+ * <code>message</code> and <code>cause</code>.
+ *
+ * @param message
+ * The detail message of this exception.
+ * @param cause
+ * The cause for this exception.
+ */
+ public PuppeTorException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a <code>PuppeTorException</code> with the given detail
+ * <code>message</code>, but without a <code>cause</code>.
+ *
+ * @param message
+ * The detail message of this exception.
+ */
+ public PuppeTorException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a <code>PuppeTorException</code> with the given
+ * <code>cause</code>, but without a detail message.
+ *
+ * @param cause
+ * The cause for this exception.
+ */
+ public PuppeTorException(final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/RouterNode.java b/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
old mode 100755
new mode 100644
index d73f409..48f721f
--- a/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
+++ b/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
@@ -1,81 +1,81 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * A <code>RouterNode</code> represents a Tor process that is configured to
- * both, relay traffic from a local application to the Tor network and to route
- * traffic on behalf of remote applications. It inherits most of its
- * configuration and behavior from its superclass <code>ProxyNode</code> and
- * adds some router-specific configurations and behavior.
- *
- * @author kloesing
- */
-public interface RouterNode extends ProxyNode {
-
- /**
- * Returns the dir port of this node.
- *
- * @return The dir port of this node.
- */
- public abstract int getDirPort();
-
- /**
- * Returns the onion port of this node.
- *
- * @return The onion port of this node.
- */
- public abstract int getOrPort();
-
- /**
- * <p>
- * Returns the fingerprint string of this node, formatted like
- * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
- * </p>
- *
- * <p>
- * The fingerprint is determined by a background thread that is started as
- * soon as the node is instantiated. If this background thread has not
- * finished when this method is invoked, the invoking thread will be blocked
- * until the fingerprint is available (or determining it has failed,
- * whereupon an exception will be thrown).
- * </p>
- *
- * @return The fingerprint of this node.
- * @throws PuppeTorException
- * Thrown if either the temporary <code>torrc.temp</code>
- * configuration file cannot be written, the Tor process cannot
- * be started temporarily, or the fingerprint file cannot be
- * read.
- */
- public abstract String getFingerprint() throws PuppeTorException;
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A <code>RouterNode</code> represents a Tor process that is configured to
+ * both, relay traffic from a local application to the Tor network and to route
+ * traffic on behalf of remote applications. It inherits most of its
+ * configuration and behavior from its superclass <code>ProxyNode</code> and
+ * adds some router-specific configurations and behavior.
+ *
+ * @author kloesing
+ */
+public interface RouterNode extends ProxyNode {
+
+ /**
+ * Returns the dir port of this node.
+ *
+ * @return The dir port of this node.
+ */
+ public abstract int getDirPort();
+
+ /**
+ * Returns the onion port of this node.
+ *
+ * @return The onion port of this node.
+ */
+ public abstract int getOrPort();
+
+ /**
+ * <p>
+ * Returns the fingerprint string of this node, formatted like
+ * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+ * </p>
+ *
+ * <p>
+ * The fingerprint is determined by a background thread that is started as
+ * soon as the node is instantiated. If this background thread has not
+ * finished when this method is invoked, the invoking thread will be blocked
+ * until the fingerprint is available (or determining it has failed,
+ * whereupon an exception will be thrown).
+ * </p>
+ *
+ * @return The fingerprint of this node.
+ * @throws PuppeTorException
+ * Thrown if either the temporary <code>torrc.temp</code>
+ * configuration file cannot be written, the Tor process cannot
+ * be started temporarily, or the fingerprint file cannot be
+ * read.
+ */
+ public abstract String getFingerprint() throws PuppeTorException;
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java b/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
old mode 100755
new mode 100644
index 3f78611..07bd717
--- a/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
+++ b/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
@@ -1,84 +1,84 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>ServerApplication</code> can be used as simple HTTP server that
- * answers all <code>HTTP GET</code> requests by empty <code>HTTP OK</code>
- * replies. Therefore, a thread will be started to listen for incoming requests
- * in the background.
- *
- * @author kloesing
- */
-public interface ServerApplication {
-
- /**
- * Starts listening for incoming <code>HTTP GET</code> requests from
- * clients. Any incoming request is answered by an empty
- * <code>HTTP OK</code> reply. This method may only be invoked when the
- * server is currently not in listening state!
- *
- * @throws IllegalStateException
- * Thrown if the server is currently not in listening state.
- */
- public abstract void startListening();
-
- /**
- * Stops listening for requests. This method may only be invoked when the
- * server is currently in listening state!
- *
- * @throws IllegalStateException
- * Thrown if the server is currently in listening state.
- */
- public abstract void stopListening();
-
- /**
- * Returns whether this server is currently in listening state.
- *
- * @return The listening state of this server.
- */
- public abstract boolean isListening();
-
- /**
- * Returns the name of this server.
- *
- * @return The name of this server.
- */
- public abstract String getServerApplicationName();
-
- /**
- * Returns the port on which this server listens.
- *
- * @return The port on which this server listens.
- */
- public abstract int getServerPort();
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ServerApplication</code> can be used as simple HTTP server that
+ * answers all <code>HTTP GET</code> requests by empty <code>HTTP OK</code>
+ * replies. Therefore, a thread will be started to listen for incoming requests
+ * in the background.
+ *
+ * @author kloesing
+ */
+public interface ServerApplication {
+
+ /**
+ * Starts listening for incoming <code>HTTP GET</code> requests from
+ * clients. Any incoming request is answered by an empty
+ * <code>HTTP OK</code> reply. This method may only be invoked when the
+ * server is currently not in listening state!
+ *
+ * @throws IllegalStateException
+ * Thrown if the server is currently not in listening state.
+ */
+ public abstract void startListening();
+
+ /**
+ * Stops listening for requests. This method may only be invoked when the
+ * server is currently in listening state!
+ *
+ * @throws IllegalStateException
+ * Thrown if the server is currently in listening state.
+ */
+ public abstract void stopListening();
+
+ /**
+ * Returns whether this server is currently in listening state.
+ *
+ * @return The listening state of this server.
+ */
+ public abstract boolean isListening();
+
+ /**
+ * Returns the name of this server.
+ *
+ * @return The name of this server.
+ */
+ public abstract String getServerApplicationName();
+
+ /**
+ * Returns the port on which this server listens.
+ *
+ * @return The port on which this server listens.
+ */
+ public abstract int getServerPort();
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java b/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
old mode 100755
new mode 100644
index e8b93c0..81a7573
--- a/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
+++ b/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
@@ -1,145 +1,145 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.examples;
-
-import de.uniba.wiai.lspi.puppetor.ClientApplication;
-import de.uniba.wiai.lspi.puppetor.ClientEventType;
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NetworkFactory;
-import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-
-/**
- * Example for accessing a public web server (here: <code>www.google.com</code>)
- * over Tor to measure the access time.
- *
- * @author kloesing
- */
-public class AccessingPublicWebServerOverTor {
-
- /**
- * Sets up and runs the test.
- *
- * @param args
- * Command-line arguments (ignored).
- * @throws PuppeTorException
- * Thrown if there is a problem with the JVM-external Tor
- * processes that we cannot handle.
- */
- public static void main(final String[] args) throws PuppeTorException {
-
- // though we only need a single proxy, we always need to create a
- // network to initialize a test case.
- final Network network = NetworkFactory.createNetwork("example1");
-
- // create a single proxy node with name "proxy"
- final ProxyNode proxy = network.createProxy("proxy");
-
- // write configuration of proxy node
- network.writeConfigurations();
-
- // start proxy node and wait until it has opened a circuit with a
- // timeout of 5 seconds
- if (!network.startNodes(5000)) {
-
- // failed to start the proxy
- System.out.println("Failed to start the node!");
- return;
- }
- System.out.println("Successfully started the node!");
-
- // hup until proxy has built circuits (5 retries, 10 seconds timeout
- // each)
- if (!network.hupUntilUp(5, 10000)) {
-
- // failed to build circuits
- System.out.println("Failed to build circuits!");
- System.exit(0);
- }
- System.out.println("Successfully built circuits!");
-
- // create client application
- final ClientApplication client =
- network.createClient("client", "www.google.com", 80, proxy
- .getSocksPort());
-
- // create event listener to listen for client application events
- final EventListener clientEventListener = new EventListener() {
-
- // remember time when request was sent
- private long before;
-
- public void handleEvent(Event event) {
- if (event.getType() == ClientEventType.CLIENT_SENDING_REQUEST) {
- before = System.currentTimeMillis();
- } else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
- System.out.println("Request took "
- + (System.currentTimeMillis() - before)
- + " milliseconds");
- }
- }
- };
-
- // obtain reference to event manager to be able to respond to events
- final EventManager manager = network.getEventManager();
-
- // register event handler for client application events
- manager.addEventListener(client.getClientApplicationName(),
- clientEventListener);
-
- // perform at most three request with a timeout of 20 seconds each
- client.startRequests(3, 20000, true);
-
- // block this thread as long as client requests are running
- manager.waitForAnyOccurence(client.getClientApplicationName(),
- ClientEventType.CLIENT_REQUESTS_PERFORMED);
-
- // wait a second before shutting down the proxy
- try {
- Thread.sleep(1000);
- } catch (final InterruptedException e) {}
-
- // shut down proxy
- network.shutdownNodes();
-
- // wait another second before exiting the application
- try {
- Thread.sleep(1000);
- } catch (final InterruptedException e) {}
-
- // Shut down the JVM
- System.out.println("Goodbye.");
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.ClientEventType;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Example for accessing a public web server (here: <code>www.google.com</code>)
+ * over Tor to measure the access time.
+ *
+ * @author kloesing
+ */
+public class AccessingPublicWebServerOverTor {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments (ignored).
+ * @throws PuppeTorException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(final String[] args) throws PuppeTorException {
+
+ // though we only need a single proxy, we always need to create a
+ // network to initialize a test case.
+ final Network network = NetworkFactory.createNetwork("example1");
+
+ // create a single proxy node with name "proxy"
+ final ProxyNode proxy = network.createProxy("proxy");
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start the node!");
+ return;
+ }
+ System.out.println("Successfully started the node!");
+
+ // hup until proxy has built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ System.exit(0);
+ }
+ System.out.println("Successfully built circuits!");
+
+ // create client application
+ final ClientApplication client =
+ network.createClient("client", "www.google.com", 80, proxy
+ .getSocksPort());
+
+ // create event listener to listen for client application events
+ final EventListener clientEventListener = new EventListener() {
+
+ // remember time when request was sent
+ private long before;
+
+ public void handleEvent(Event event) {
+ if (event.getType() == ClientEventType.CLIENT_SENDING_REQUEST) {
+ before = System.currentTimeMillis();
+ } else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
+ System.out.println("Request took "
+ + (System.currentTimeMillis() - before)
+ + " milliseconds");
+ }
+ }
+ };
+
+ // obtain reference to event manager to be able to respond to events
+ final EventManager manager = network.getEventManager();
+
+ // register event handler for client application events
+ manager.addEventListener(client.getClientApplicationName(),
+ clientEventListener);
+
+ // perform at most three request with a timeout of 20 seconds each
+ client.startRequests(3, 20000, true);
+
+ // block this thread as long as client requests are running
+ manager.waitForAnyOccurence(client.getClientApplicationName(),
+ ClientEventType.CLIENT_REQUESTS_PERFORMED);
+
+ // wait a second before shutting down the proxy
+ try {
+ Thread.sleep(1000);
+ } catch (final InterruptedException e) {}
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ // wait another second before exiting the application
+ try {
+ Thread.sleep(1000);
+ } catch (final InterruptedException e) {}
+
+ // Shut down the JVM
+ System.out.println("Goodbye.");
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
old mode 100755
new mode 100644
index 0136c7e..10f9cd1
--- a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
+++ b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
@@ -1,158 +1,158 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.examples;
-
-import de.uniba.wiai.lspi.puppetor.ClientApplication;
-import de.uniba.wiai.lspi.puppetor.ClientEventType;
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.HiddenService;
-import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
-import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NetworkFactory;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-import de.uniba.wiai.lspi.puppetor.RouterNode;
-import de.uniba.wiai.lspi.puppetor.ServerApplication;
-
-/**
- * Example for advertising and accessing a hidden service over a private Tor
- * network.
- *
- * @author kloesing
- */
-public class AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork {
-
- /**
- * Sets up and runs the test.
- *
- * @param args
- * Command-line arguments (ignored).
- * @throws PuppeTorException
- * Thrown if there is a problem with the JVM-external Tor
- * processes that we cannot handle.
- */
- public static void main(final String[] args) throws PuppeTorException {
-
- // create a network to initialize the test case
- final Network network = NetworkFactory.createNetwork("example4");
-
- // create three router nodes
- final RouterNode router1 = network.createRouter("router1");
- network.createRouter("router2");
- final RouterNode router3 = network.createRouter("router3");
-
- // create only one directory node
- network.createDirectory("dir1");
-
- // add hidden service
- final HiddenService hidServ1 = router1.addHiddenService("hidServ");
-
- // configure nodes of this network to be part of a private network
- network.configureAsPrivateNetwork();
-
- // write node configurations
- network.writeConfigurations();
-
- // start nodes and wait until they have opened a circuit with a timeout
- // of 5 seconds
- if (!network.startNodes(5000)) {
-
- // failed to start the nodes
- System.out.println("Failed to start nodes!");
- System.exit(0);
- }
- System.out.println("Successfully started nodes!");
-
- // hup until nodes have built circuits (60 retries, 10 seconds timeout
- // each)
- if (!network.hupUntilUp(60, 10000)) {
-
- // failed to build circuits
- System.out.println("Failed to build circuits!");
- System.exit(0);
- }
- System.out.println("Successfully built circuits!");
-
- // obtain reference to event manager to be able to respond to events
- final EventManager manager = network.getEventManager();
-
- // wait for 1 hour that the proxy has published its first RSD
- if (!manager.waitForAnyOccurence(router1.getNodeName(),
- HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
- 1L * 60L * 60L * 1000L)) {
- // failed to publish an RSD
- System.out.println("Failed to publish an RSD!");
- System.exit(0);
- }
- System.out.println("Successfully published an RSD!");
-
- // create server application
- final ServerApplication server =
- network.createServer("server", hidServ1.getServicePort());
-
- // create client application
- final ClientApplication client =
- network.createClient("client",
- hidServ1.determineOnionAddress(), hidServ1
- .getVirtualPort(), router3.getSocksPort());
-
- // register event listener
- final EventListener clientAndServerEventListener = new EventListener() {
- public void handleEvent(Event event) {
- System.out.println("Handling event: " + event.getMessage());
- }
- };
- manager.addEventListener(client.getClientApplicationName(),
- clientAndServerEventListener);
- manager.addEventListener(server.getServerApplicationName(),
- clientAndServerEventListener);
-
- // start server
- server.startListening();
- System.out.println("Started server");
-
- // perform at most five request with a timeout of 45 seconds each
- client.startRequests(5, 45000, true);
-
- // wait for request to be performed
- manager.waitForAnyOccurence(client.getClientApplicationName(),
- ClientEventType.CLIENT_REQUESTS_PERFORMED);
-
- // shut down nodes
- network.shutdownNodes();
-
- // Shut down the JVM
- System.out.println("Goodbye.");
- System.exit(0);
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.ClientEventType;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+
+/**
+ * Example for advertising and accessing a hidden service over a private Tor
+ * network.
+ *
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments (ignored).
+ * @throws PuppeTorException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(final String[] args) throws PuppeTorException {
+
+ // create a network to initialize the test case
+ final Network network = NetworkFactory.createNetwork("example4");
+
+ // create three router nodes
+ final RouterNode router1 = network.createRouter("router1");
+ network.createRouter("router2");
+ final RouterNode router3 = network.createRouter("router3");
+
+ // create only one directory node
+ network.createDirectory("dir1");
+
+ // add hidden service
+ final HiddenService hidServ1 = router1.addHiddenService("hidServ");
+
+ // configure nodes of this network to be part of a private network
+ network.configureAsPrivateNetwork();
+
+ // write node configurations
+ network.writeConfigurations();
+
+ // start nodes and wait until they have opened a circuit with a timeout
+ // of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the nodes
+ System.out.println("Failed to start nodes!");
+ System.exit(0);
+ }
+ System.out.println("Successfully started nodes!");
+
+ // hup until nodes have built circuits (60 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(60, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ System.exit(0);
+ }
+ System.out.println("Successfully built circuits!");
+
+ // obtain reference to event manager to be able to respond to events
+ final EventManager manager = network.getEventManager();
+
+ // wait for 1 hour that the proxy has published its first RSD
+ if (!manager.waitForAnyOccurence(router1.getNodeName(),
+ HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
+ 1L * 60L * 60L * 1000L)) {
+ // failed to publish an RSD
+ System.out.println("Failed to publish an RSD!");
+ System.exit(0);
+ }
+ System.out.println("Successfully published an RSD!");
+
+ // create server application
+ final ServerApplication server =
+ network.createServer("server", hidServ1.getServicePort());
+
+ // create client application
+ final ClientApplication client =
+ network.createClient("client",
+ hidServ1.determineOnionAddress(), hidServ1
+ .getVirtualPort(), router3.getSocksPort());
+
+ // register event listener
+ final EventListener clientAndServerEventListener = new EventListener() {
+ public void handleEvent(Event event) {
+ System.out.println("Handling event: " + event.getMessage());
+ }
+ };
+ manager.addEventListener(client.getClientApplicationName(),
+ clientAndServerEventListener);
+ manager.addEventListener(server.getServerApplicationName(),
+ clientAndServerEventListener);
+
+ // start server
+ server.startListening();
+ System.out.println("Started server");
+
+ // perform at most five request with a timeout of 45 seconds each
+ client.startRequests(5, 45000, true);
+
+ // wait for request to be performed
+ manager.waitForAnyOccurence(client.getClientApplicationName(),
+ ClientEventType.CLIENT_REQUESTS_PERFORMED);
+
+ // shut down nodes
+ network.shutdownNodes();
+
+ // Shut down the JVM
+ System.out.println("Goodbye.");
+ System.exit(0);
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
old mode 100755
new mode 100644
index 5a521f5..c639e3d
--- a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
+++ b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
@@ -1,175 +1,175 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.examples;
-
-import de.uniba.wiai.lspi.puppetor.ClientApplication;
-import de.uniba.wiai.lspi.puppetor.ClientEventType;
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.HiddenService;
-import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
-import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NetworkFactory;
-import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-import de.uniba.wiai.lspi.puppetor.ServerApplication;
-import de.uniba.wiai.lspi.puppetor.ServerEventType;
-
-/**
- * Example for advertising and accessing a hidden service over the public Tor
- * network.
- *
- * @author kloesing
- */
-public class AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork {
-
- /**
- * Sets up and runs the test.
- *
- * @param args
- * Command-line arguments (ignored).
- * @throws PuppeTorException
- * Thrown if there is a problem with the JVM-external Tor
- * processes that we cannot handle.
- */
- public static void main(final String[] args) throws PuppeTorException {
-
- // create a network to initialize the test case
- final Network network = NetworkFactory.createNetwork("example3");
-
- // create two proxy nodes
- final ProxyNode proxy1 = network.createProxy("proxy1");
- final ProxyNode proxy2 = network.createProxy("proxy2");
-
- // add hidden service to the configuration of proxy1
- final HiddenService hidServ1 = proxy1.addHiddenService("hidServ");
-
- // write configuration of proxy nodes
- network.writeConfigurations();
-
- // start nodes and wait until they have opened a circuit with a timeout
- // of 5 seconds
- if (!network.startNodes(5000)) {
-
- // failed to start the proxy
- System.out.println("Failed to start nodes!");
- System.exit(0);
- }
- System.out.println("Successfully started nodes!");
-
- // hup until nodes have built circuits (5 retries, 10 seconds timeout
- // each)
- if (!network.hupUntilUp(5, 10000)) {
-
- // failed to build circuits
- System.out.println("Failed to build circuits!");
- System.exit(0);
- }
- System.out.println("Successfully built circuits!");
-
- // obtain reference to event manager to be able to respond to events
- final EventManager manager = network.getEventManager();
-
- // wait for 3 minutes that the proxy has published its first RSD
- if (!manager.waitForAnyOccurence(proxy1.getNodeName(),
- HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
- 3L * 60L * 1000L)) {
-
- // failed to publish an RSD
- System.out.println("Failed to publish an RSD!");
- System.exit(0);
- }
- System.out.println("Successfully published an RSD!");
-
- // create server application
- final ServerApplication server =
- network.createServer("server", hidServ1.getServicePort());
-
- // create client application
- final ClientApplication client =
- network.createClient("client",
- hidServ1.determineOnionAddress(), hidServ1
- .getVirtualPort(), proxy2.getSocksPort());
-
- // create event listener to listen for client and server application
- // events
- final EventListener clientAndServerEventListener = new EventListener() {
-
- private long requestReceivedAtServer;
-
- // remember time when request was sent and when it was received
- private long requestSentFromClient;
-
- public void handleEvent(Event event) {
- if (event.getType() == ClientEventType.CLIENT_SENDING_REQUEST) {
- requestSentFromClient = event.getOccurrenceTime();
- } else if (event.getType() == ServerEventType.SERVER_RECEIVING_REQUEST_SENDING_REPLY) {
- requestReceivedAtServer = event.getOccurrenceTime();
- System.out.println("Request took "
- + (requestReceivedAtServer - requestSentFromClient)
- + " milliseconds from client to server!");
- } else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
- System.out
- .println("Request took "
- + (event.getOccurrenceTime() - requestSentFromClient)
- + " milliseconds for the round-trip and "
- + (event.getOccurrenceTime() - requestReceivedAtServer)
- + " milliseconds from server to client!");
- }
- }
- };
-
- // register event handler for client and server application events
- manager.addEventListener(client.getClientApplicationName(),
- clientAndServerEventListener);
- manager.addEventListener(server.getServerApplicationName(),
- clientAndServerEventListener);
-
- // start server
- server.startListening();
-
- // perform at most five request with a timeout of 45 seconds each
- client.startRequests(5, 45000, true);
-
- // block this thread as long as client requests are running
- manager.waitForAnyOccurence(client.getClientApplicationName(),
- ClientEventType.CLIENT_REQUESTS_PERFORMED);
-
- // shut down proxy
- network.shutdownNodes();
-
- // Shut down the JVM
- System.out.println("Goodbye.");
- System.exit(0);
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.ClientEventType;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.ServerEventType;
+
+/**
+ * Example for advertising and accessing a hidden service over the public Tor
+ * network.
+ *
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments (ignored).
+ * @throws PuppeTorException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(final String[] args) throws PuppeTorException {
+
+ // create a network to initialize the test case
+ final Network network = NetworkFactory.createNetwork("example3");
+
+ // create two proxy nodes
+ final ProxyNode proxy1 = network.createProxy("proxy1");
+ final ProxyNode proxy2 = network.createProxy("proxy2");
+
+ // add hidden service to the configuration of proxy1
+ final HiddenService hidServ1 = proxy1.addHiddenService("hidServ");
+
+ // write configuration of proxy nodes
+ network.writeConfigurations();
+
+ // start nodes and wait until they have opened a circuit with a timeout
+ // of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start nodes!");
+ System.exit(0);
+ }
+ System.out.println("Successfully started nodes!");
+
+ // hup until nodes have built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ System.exit(0);
+ }
+ System.out.println("Successfully built circuits!");
+
+ // obtain reference to event manager to be able to respond to events
+ final EventManager manager = network.getEventManager();
+
+ // wait for 3 minutes that the proxy has published its first RSD
+ if (!manager.waitForAnyOccurence(proxy1.getNodeName(),
+ HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
+ 3L * 60L * 1000L)) {
+
+ // failed to publish an RSD
+ System.out.println("Failed to publish an RSD!");
+ System.exit(0);
+ }
+ System.out.println("Successfully published an RSD!");
+
+ // create server application
+ final ServerApplication server =
+ network.createServer("server", hidServ1.getServicePort());
+
+ // create client application
+ final ClientApplication client =
+ network.createClient("client",
+ hidServ1.determineOnionAddress(), hidServ1
+ .getVirtualPort(), proxy2.getSocksPort());
+
+ // create event listener to listen for client and server application
+ // events
+ final EventListener clientAndServerEventListener = new EventListener() {
+
+ private long requestReceivedAtServer;
+
+ // remember time when request was sent and when it was received
+ private long requestSentFromClient;
+
+ public void handleEvent(Event event) {
+ if (event.getType() == ClientEventType.CLIENT_SENDING_REQUEST) {
+ requestSentFromClient = event.getOccurrenceTime();
+ } else if (event.getType() == ServerEventType.SERVER_RECEIVING_REQUEST_SENDING_REPLY) {
+ requestReceivedAtServer = event.getOccurrenceTime();
+ System.out.println("Request took "
+ + (requestReceivedAtServer - requestSentFromClient)
+ + " milliseconds from client to server!");
+ } else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
+ System.out
+ .println("Request took "
+ + (event.getOccurrenceTime() - requestSentFromClient)
+ + " milliseconds for the round-trip and "
+ + (event.getOccurrenceTime() - requestReceivedAtServer)
+ + " milliseconds from server to client!");
+ }
+ }
+ };
+
+ // register event handler for client and server application events
+ manager.addEventListener(client.getClientApplicationName(),
+ clientAndServerEventListener);
+ manager.addEventListener(server.getServerApplicationName(),
+ clientAndServerEventListener);
+
+ // start server
+ server.startListening();
+
+ // perform at most five request with a timeout of 45 seconds each
+ client.startRequests(5, 45000, true);
+
+ // block this thread as long as client requests are running
+ manager.waitForAnyOccurence(client.getClientApplicationName(),
+ ClientEventType.CLIENT_REQUESTS_PERFORMED);
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ // Shut down the JVM
+ System.out.println("Goodbye.");
+ System.exit(0);
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
old mode 100755
new mode 100644
index 0c7564c..0091585
--- a/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
+++ b/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
@@ -1,136 +1,136 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.examples;
-
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
-import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NetworkFactory;
-import de.uniba.wiai.lspi.puppetor.NodeEventType;
-import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-
-/**
- * Example for advertising a hidden service to the public Tor network and
- * observing the publication of rendezvous service descriptors.
- *
- * @author kloesing
- */
-public class AdvertisingHiddenServiceToPublicTorNetwork {
-
- /**
- * Sets up and runs the test.
- *
- * @param args
- * Command-line arguments (ignored).
- * @throws PuppeTorException
- * Thrown if there is a problem with the JVM-external Tor
- * processes that we cannot handle.
- */
- public static void main(final String[] args) throws PuppeTorException {
- // create a network to initialize the test case
- final Network network = NetworkFactory.createNetwork("example2");
-
- // create a single proxy node
- final ProxyNode proxy = network.createProxy("proxy");
-
- // add hidden service to the configuration
- proxy.addHiddenService("hidServ");
-
- // write configuration of proxy node
- network.writeConfigurations();
-
- // create event listener to listen for events from our proxy
- final EventListener proxyEventListener = new EventListener() {
-
- // remember time when request was sent
- private long circuitOpened = -1;
-
- public void handleEvent(Event event) {
- if (event.getType() == NodeEventType.NODE_CIRCUIT_OPENED) {
- if (circuitOpened == -1) {
- circuitOpened = System.currentTimeMillis();
- }
- } else if (event.getType() == HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED) {
- System.out.println("RSD published "
- + (System.currentTimeMillis() - circuitOpened)
- + " milliseconds after first circuit was opened");
- }
- }
- };
-
- // obtain reference to event manager to be able to respond to events
- final EventManager manager = network.getEventManager();
-
- // register event handler for proxy events
- manager.addEventListener(proxy.getNodeName(), proxyEventListener);
-
- // start proxy node and wait until it has opened a circuit with a
- // timeout of 5 seconds
- if (!network.startNodes(5000)) {
-
- // failed to start the proxy
- System.out.println("Failed to start the node!");
- System.exit(0);
- }
- System.out.println("Successfully started the node!");
-
- // hup until proxy has built circuits (5 retries, 10 seconds timeout
- // each)
- if (!network.hupUntilUp(5, 10000)) {
-
- // failed to build circuits
- System.out.println("Failed to build circuits!");
- System.exit(0);
- }
- System.out.println("Successfully built circuits!");
-
- // let it run for 2 minutes and observe when RSDs are published...
- System.out
- .println("Waiting for 2 minutes and observing RSD publications...");
-
- try {
- Thread.sleep(2L * 60L * 1000L);
- } catch (final InterruptedException e) {
- // do nothing
- }
-
- // shut down proxy
- network.shutdownNodes();
-
- // Shut down the JVM
- System.out.println("Goodbye.");
- System.exit(0);
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.NodeEventType;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Example for advertising a hidden service to the public Tor network and
+ * observing the publication of rendezvous service descriptors.
+ *
+ * @author kloesing
+ */
+public class AdvertisingHiddenServiceToPublicTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments (ignored).
+ * @throws PuppeTorException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(final String[] args) throws PuppeTorException {
+ // create a network to initialize the test case
+ final Network network = NetworkFactory.createNetwork("example2");
+
+ // create a single proxy node
+ final ProxyNode proxy = network.createProxy("proxy");
+
+ // add hidden service to the configuration
+ proxy.addHiddenService("hidServ");
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // create event listener to listen for events from our proxy
+ final EventListener proxyEventListener = new EventListener() {
+
+ // remember time when request was sent
+ private long circuitOpened = -1;
+
+ public void handleEvent(Event event) {
+ if (event.getType() == NodeEventType.NODE_CIRCUIT_OPENED) {
+ if (circuitOpened == -1) {
+ circuitOpened = System.currentTimeMillis();
+ }
+ } else if (event.getType() == HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED) {
+ System.out.println("RSD published "
+ + (System.currentTimeMillis() - circuitOpened)
+ + " milliseconds after first circuit was opened");
+ }
+ }
+ };
+
+ // obtain reference to event manager to be able to respond to events
+ final EventManager manager = network.getEventManager();
+
+ // register event handler for proxy events
+ manager.addEventListener(proxy.getNodeName(), proxyEventListener);
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start the node!");
+ System.exit(0);
+ }
+ System.out.println("Successfully started the node!");
+
+ // hup until proxy has built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ System.exit(0);
+ }
+ System.out.println("Successfully built circuits!");
+
+ // let it run for 2 minutes and observe when RSDs are published...
+ System.out
+ .println("Waiting for 2 minutes and observing RSD publications...");
+
+ try {
+ Thread.sleep(2L * 60L * 1000L);
+ } catch (final InterruptedException e) {
+ // do nothing
+ }
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ // Shut down the JVM
+ System.out.println("Goodbye.");
+ System.exit(0);
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
old mode 100755
new mode 100644
index df7667f..f42a37a
--- a/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
@@ -1,483 +1,483 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.net.Proxy.Type;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.uniba.wiai.lspi.puppetor.ClientApplication;
-import de.uniba.wiai.lspi.puppetor.ClientEventType;
-
-/**
- * Implementation of <code>ClientApplication</code>.
- *
- * @author kloesing
- */
-public class ClientApplicationImpl implements ClientApplication {
-
- /**
- * Internal thread class that is used to perform requests.
- */
- private class RequestThread extends Thread {
-
- /**
- * Flag to remember whether requests are performed at the moment (<code>true</code>),
- * or have been stopped (<code>false</code>).
- */
- private boolean connected;
-
- /**
- * Number of retries to be performed.
- */
- private final int retries;
-
- /**
- * Flag that determines whether requests shall be stopped after the
- * first successful reply (<code>true</code>), or not (<code>false</code>).
- */
- private final boolean stopOnSuccess;
-
- /**
- * Timeout in milliseconds for each retry.
- */
- private final long timeoutForEachRetry;
-
- /**
- * Creates a new thread, but does not start performing requests, yet.
- *
- * @param retries
- * Number of retries to be performed.
- * @param timeoutForEachRetry
- * Timeout in milliseconds for each retry.
- * @param stopOnSuccess
- * Flag that determines whether requests shall be stopped
- * after the first successful reply (<code>true</code>),
- * or not (<code>false</code>).
- */
- RequestThread(final int retries, final long timeoutForEachRetry,
- final boolean stopOnSuccess) {
-
- // log entering
- logger
- .entering(this.getClass().getName(), "RequestThread",
- new Object[] { retries, timeoutForEachRetry,
- stopOnSuccess });
-
- // check parameters
- if (retries < 0 || timeoutForEachRetry < 0) {
- final IllegalArgumentException e =
- new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "RequestThread", e);
- throw e;
- }
-
- // remember parameters
- this.retries = retries;
- this.timeoutForEachRetry = timeoutForEachRetry;
- this.stopOnSuccess = stopOnSuccess;
-
- // start connected
- connected = true;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "RequestThread");
- }
-
- /**
- * Perform one or more requests.
- */
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- try {
-
- // set Tor as proxy
- final InetSocketAddress isa =
- new InetSocketAddress("127.0.0.1", socksPort);
- final Proxy p = new Proxy(Type.SOCKS, isa);
-
- // create target address for socket -- don't resolve the target
- // name to an IP address!
- final InetSocketAddress hs =
- InetSocketAddress.createUnresolved(targetName,
- targetPort);
-
- // start retry loop
- for (int i = 0; connected && i < retries; i++) {
-
- // log this try
- logger.log(Level.FINE, "Trying to perform request");
-
- // remember when we started
- final long timeBeforeConnectionAttempt =
- System.currentTimeMillis();
-
- // send event to event manager
- eventManager.observeInternalEvent(
- timeBeforeConnectionAttempt,
- getClientApplicationName(),
- ClientEventType.CLIENT_SENDING_REQUEST,
- "Sending request.");
-
- Socket s = null;
- try {
-
- // create new socket using Tor as proxy
- s = new Socket(p);
-
- // try to connect to remote server
- s.connect(hs, (int) timeoutForEachRetry);
-
- // open output stream to write request
- final PrintStream out =
- new PrintStream(s.getOutputStream());
- out.print("GET / HTTP/1.0\r\n\r\n");
-
- // open input stream to read reply
- final BufferedReader in =
- new BufferedReader(new InputStreamReader(s
- .getInputStream()));
-
- // only read the first char in the response; this method
- // blocks until there is a response
- in.read();
-
- // send event to event manager
- eventManager.observeInternalEvent(System
- .currentTimeMillis(),
- getClientApplicationName(),
- ClientEventType.CLIENT_REPLY_RECEIVED,
- "Received response.");
-
- // if we should stop on success, stop further connection
- // attempts
- if (stopOnSuccess) {
- connected = false;
- }
-
- // clean up socket
- in.close();
- out.close();
- s.close();
-
- } catch (final SocketTimeoutException e) {
-
- // log warning
- logger.log(Level.WARNING,
- "Connection to remote server timed out!", e);
-
- // send event to event manager
- eventManager.observeInternalEvent(System
- .currentTimeMillis(),
- getClientApplicationName(),
- ClientEventType.CLIENT_GAVE_UP_REQUEST,
- "Giving up request.");
-
- // try again immediately, if there are retries left
-
- } catch (final IOException e) {
-
- // log warning
- logger.log(Level.WARNING,
- "Connection to remote server could not be "
- + "established!", e);
-
- // send event to event manager
- eventManager.observeInternalEvent(System
- .currentTimeMillis(),
- getClientApplicationName(),
- ClientEventType.CLIENT_GAVE_UP_REQUEST,
- "Giving up request.");
-
- // wait for the rest of the timeout
- final long timeOfTimeoutLeft =
- timeBeforeConnectionAttempt
- + timeoutForEachRetry
- - System.currentTimeMillis();
- if (timeOfTimeoutLeft > 0) {
- try {
- Thread.sleep(timeOfTimeoutLeft);
- } catch (final InterruptedException ex) {
- // do nothing
- }
- }
-
- } finally {
- if (s != null) {
- // close connection
- try {
-
- // try to close socket
- logger.log(Level.FINER,
- "Trying to close socket.");
- s.close();
- logger.log(Level.FINE, "Socket closed.");
-
- } catch (final IOException e) {
-
- // log warning
- logger.log(Level.WARNING,
- "Exception when trying to close socket!",
- e);
- }
- }
- }
- }
-
- } finally {
-
- // we are done here
- logger.log(Level.FINE, "Requests performed!");
-
- // send event to event manager
- eventManager.observeInternalEvent(System.currentTimeMillis(),
- getClientApplicationName(),
- ClientEventType.CLIENT_REQUESTS_PERFORMED,
- "Requests performed.");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- }
-
- /**
- * Immediately stops this and all possibly subsequent requests.
- */
- public void stopRequest() {
-
- // log entering
- logger.entering(this.getClass().getName(), "stopRequest");
-
- // change connected state to false and interrupt thread
- connected = false;
- interrupt();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "stopRequest");
- }
- }
-
- /**
- * Name of this client application that is used as logger name of this node.
- */
- private final String clientApplicationName;
-
- /**
- * Thread that performs the requests in the background.
- */
- private RequestThread clientThread;
-
- /**
- * Event manager that handles all events concerning this client application.
- */
- private final EventManagerImpl eventManager;
-
- /**
- * Logger for this client which is called "client." plus the name of this
- * client application.
- */
- private final Logger logger;
-
- /**
- * SOCKS port of the local Tor node to which requests are sent.
- */
- private final int socksPort;
-
- /**
- * Target name for the requests sent by this client; can be a publicly
- * available URL or an onion address.
- */
- private final String targetName;
-
- /**
- * Target port for the requests sent by this client; can be either a server
- * port or a virtual port of a hidden service.
- */
- private final int targetPort;
-
- /**
- * Creates a new HTTP client within this JVM, but does not start sending
- * requests.
- *
- * @param network
- * Network to which this HTTP client belongs; at the moment this
- * is only used to determine the event manager instance.
- * @param clientApplicationName
- * Name of this client that is used as part of the logger name.
- * @param targetName
- * Target name for requests; can be either a server name/address
- * or an onion address.
- * @param targetPort
- * Target port for requests; can be either a server port or a
- * virtual port of a hidden service.
- * @param socksPort
- * SOCKS port of the local Tor node.
- * @throws IllegalArgumentException
- * If at least one of the parameters is <code>null</code> or
- * has an invalid value.
- */
- ClientApplicationImpl(final NetworkImpl network,
- final String clientApplicationName, final String targetName,
- final int targetPort, final int socksPort) {
-
- // check if clientApplicationName can be used as logger name
- if (clientApplicationName == null
- || clientApplicationName.length() == 0) {
- throw new IllegalArgumentException(
- "Invalid clientApplicationName: " + clientApplicationName);
- }
-
- // create logger
- logger = Logger.getLogger("client." + clientApplicationName);
-
- // log entering
- logger.entering(this.getClass().getName(), "ClientApplicationImpl",
- new Object[] { network, clientApplicationName, targetName,
- targetPort, socksPort });
-
- // check parameters
- if (network == null || targetName == null || targetName.length() == 0
- || targetPort < 0 || targetPort > 65535 || socksPort < 0
- || socksPort > 65535) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "ClientApplicationImpl",
- e);
- throw e;
- }
-
- // remember parameters
- this.clientApplicationName = clientApplicationName;
- this.targetName = targetName;
- this.targetPort = targetPort;
- this.socksPort = socksPort;
-
- // obtain and store reference on event manager
- eventManager = network.getEventManagerImpl();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
- }
-
- public synchronized void startRequests(final int retries,
- final long timeoutForEachRetry, final boolean stopOnSuccess) {
-
- // log entering
- logger.entering(this.getClass().getName(), "performRequest",
- new Object[] { retries, timeoutForEachRetry, stopOnSuccess });
-
- // check parameters
- if (retries <= 0 || timeoutForEachRetry < 0) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "performRequest", e);
- throw e;
- }
-
- // check if we already have started a request (TODO change this to allow
- // multiple requests in parallel? would be possible)
- if (clientThread != null) {
- final IllegalStateException e =
- new IllegalStateException(
- "Another request has already been started!");
- logger.throwing(this.getClass().getName(), "performRequest", e);
- throw e;
- }
-
- // create a thread that performs requests in the background
- clientThread =
- new RequestThread(retries, timeoutForEachRetry, stopOnSuccess);
- clientThread.setName("Request Thread");
- clientThread.setDaemon(true);
- clientThread.start();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "performRequest");
- }
-
- public synchronized void stopRequest() {
-
- // log entering
- logger.entering(this.getClass().getName(), "stopRequest");
-
- // check if a request is running
- if (clientThread == null) {
- final IllegalStateException e =
- new IllegalStateException("Cannot stop "
- + "request, because no request has been started!");
- logger.throwing(this.getClass().getName(), "stopRequest", e);
- throw e;
- }
-
- // log this event
- logger.log(Level.FINE, "Shutting down client");
-
- // interrupt thread
- clientThread.stopRequest();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "stopRequest");
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": clientApplicationName=\""
- + clientApplicationName + "\", targetAddress=\"" + targetName
- + "\", targetPort=" + targetPort + ", socksPort=" + socksPort;
- }
-
- public String getClientApplicationName() {
- return clientApplicationName;
- }
-
- public int getSocksPort() {
- return socksPort;
- }
-
- public String getTargetName() {
- return targetName;
- }
-
- public int getTargetPort() {
- return targetPort;
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.Proxy.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.ClientEventType;
+
+/**
+ * Implementation of <code>ClientApplication</code>.
+ *
+ * @author kloesing
+ */
+public class ClientApplicationImpl implements ClientApplication {
+
+ /**
+ * Internal thread class that is used to perform requests.
+ */
+ private class RequestThread extends Thread {
+
+ /**
+ * Flag to remember whether requests are performed at the moment (<code>true</code>),
+ * or have been stopped (<code>false</code>).
+ */
+ private boolean connected;
+
+ /**
+ * Number of retries to be performed.
+ */
+ private final int retries;
+
+ /**
+ * Flag that determines whether requests shall be stopped after the
+ * first successful reply (<code>true</code>), or not (<code>false</code>).
+ */
+ private final boolean stopOnSuccess;
+
+ /**
+ * Timeout in milliseconds for each retry.
+ */
+ private final long timeoutForEachRetry;
+
+ /**
+ * Creates a new thread, but does not start performing requests, yet.
+ *
+ * @param retries
+ * Number of retries to be performed.
+ * @param timeoutForEachRetry
+ * Timeout in milliseconds for each retry.
+ * @param stopOnSuccess
+ * Flag that determines whether requests shall be stopped
+ * after the first successful reply (<code>true</code>),
+ * or not (<code>false</code>).
+ */
+ RequestThread(final int retries, final long timeoutForEachRetry,
+ final boolean stopOnSuccess) {
+
+ // log entering
+ logger
+ .entering(this.getClass().getName(), "RequestThread",
+ new Object[] { retries, timeoutForEachRetry,
+ stopOnSuccess });
+
+ // check parameters
+ if (retries < 0 || timeoutForEachRetry < 0) {
+ final IllegalArgumentException e =
+ new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "RequestThread", e);
+ throw e;
+ }
+
+ // remember parameters
+ this.retries = retries;
+ this.timeoutForEachRetry = timeoutForEachRetry;
+ this.stopOnSuccess = stopOnSuccess;
+
+ // start connected
+ connected = true;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "RequestThread");
+ }
+
+ /**
+ * Perform one or more requests.
+ */
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+
+ // set Tor as proxy
+ final InetSocketAddress isa =
+ new InetSocketAddress("127.0.0.1", socksPort);
+ final Proxy p = new Proxy(Type.SOCKS, isa);
+
+ // create target address for socket -- don't resolve the target
+ // name to an IP address!
+ final InetSocketAddress hs =
+ InetSocketAddress.createUnresolved(targetName,
+ targetPort);
+
+ // start retry loop
+ for (int i = 0; connected && i < retries; i++) {
+
+ // log this try
+ logger.log(Level.FINE, "Trying to perform request");
+
+ // remember when we started
+ final long timeBeforeConnectionAttempt =
+ System.currentTimeMillis();
+
+ // send event to event manager
+ eventManager.observeInternalEvent(
+ timeBeforeConnectionAttempt,
+ getClientApplicationName(),
+ ClientEventType.CLIENT_SENDING_REQUEST,
+ "Sending request.");
+
+ Socket s = null;
+ try {
+
+ // create new socket using Tor as proxy
+ s = new Socket(p);
+
+ // try to connect to remote server
+ s.connect(hs, (int) timeoutForEachRetry);
+
+ // open output stream to write request
+ final PrintStream out =
+ new PrintStream(s.getOutputStream());
+ out.print("GET / HTTP/1.0\r\n\r\n");
+
+ // open input stream to read reply
+ final BufferedReader in =
+ new BufferedReader(new InputStreamReader(s
+ .getInputStream()));
+
+ // only read the first char in the response; this method
+ // blocks until there is a response
+ in.read();
+
+ // send event to event manager
+ eventManager.observeInternalEvent(System
+ .currentTimeMillis(),
+ getClientApplicationName(),
+ ClientEventType.CLIENT_REPLY_RECEIVED,
+ "Received response.");
+
+ // if we should stop on success, stop further connection
+ // attempts
+ if (stopOnSuccess) {
+ connected = false;
+ }
+
+ // clean up socket
+ in.close();
+ out.close();
+ s.close();
+
+ } catch (final SocketTimeoutException e) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Connection to remote server timed out!", e);
+
+ // send event to event manager
+ eventManager.observeInternalEvent(System
+ .currentTimeMillis(),
+ getClientApplicationName(),
+ ClientEventType.CLIENT_GAVE_UP_REQUEST,
+ "Giving up request.");
+
+ // try again immediately, if there are retries left
+
+ } catch (final IOException e) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Connection to remote server could not be "
+ + "established!", e);
+
+ // send event to event manager
+ eventManager.observeInternalEvent(System
+ .currentTimeMillis(),
+ getClientApplicationName(),
+ ClientEventType.CLIENT_GAVE_UP_REQUEST,
+ "Giving up request.");
+
+ // wait for the rest of the timeout
+ final long timeOfTimeoutLeft =
+ timeBeforeConnectionAttempt
+ + timeoutForEachRetry
+ - System.currentTimeMillis();
+ if (timeOfTimeoutLeft > 0) {
+ try {
+ Thread.sleep(timeOfTimeoutLeft);
+ } catch (final InterruptedException ex) {
+ // do nothing
+ }
+ }
+
+ } finally {
+ if (s != null) {
+ // close connection
+ try {
+
+ // try to close socket
+ logger.log(Level.FINER,
+ "Trying to close socket.");
+ s.close();
+ logger.log(Level.FINE, "Socket closed.");
+
+ } catch (final IOException e) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Exception when trying to close socket!",
+ e);
+ }
+ }
+ }
+ }
+
+ } finally {
+
+ // we are done here
+ logger.log(Level.FINE, "Requests performed!");
+
+ // send event to event manager
+ eventManager.observeInternalEvent(System.currentTimeMillis(),
+ getClientApplicationName(),
+ ClientEventType.CLIENT_REQUESTS_PERFORMED,
+ "Requests performed.");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * Immediately stops this and all possibly subsequent requests.
+ */
+ public void stopRequest() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopRequest");
+
+ // change connected state to false and interrupt thread
+ connected = false;
+ interrupt();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopRequest");
+ }
+ }
+
+ /**
+ * Name of this client application that is used as logger name of this node.
+ */
+ private final String clientApplicationName;
+
+ /**
+ * Thread that performs the requests in the background.
+ */
+ private RequestThread clientThread;
+
+ /**
+ * Event manager that handles all events concerning this client application.
+ */
+ private final EventManagerImpl eventManager;
+
+ /**
+ * Logger for this client which is called "client." plus the name of this
+ * client application.
+ */
+ private final Logger logger;
+
+ /**
+ * SOCKS port of the local Tor node to which requests are sent.
+ */
+ private final int socksPort;
+
+ /**
+ * Target name for the requests sent by this client; can be a publicly
+ * available URL or an onion address.
+ */
+ private final String targetName;
+
+ /**
+ * Target port for the requests sent by this client; can be either a server
+ * port or a virtual port of a hidden service.
+ */
+ private final int targetPort;
+
+ /**
+ * Creates a new HTTP client within this JVM, but does not start sending
+ * requests.
+ *
+ * @param network
+ * Network to which this HTTP client belongs; at the moment this
+ * is only used to determine the event manager instance.
+ * @param clientApplicationName
+ * Name of this client that is used as part of the logger name.
+ * @param targetName
+ * Target name for requests; can be either a server name/address
+ * or an onion address.
+ * @param targetPort
+ * Target port for requests; can be either a server port or a
+ * virtual port of a hidden service.
+ * @param socksPort
+ * SOCKS port of the local Tor node.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ ClientApplicationImpl(final NetworkImpl network,
+ final String clientApplicationName, final String targetName,
+ final int targetPort, final int socksPort) {
+
+ // check if clientApplicationName can be used as logger name
+ if (clientApplicationName == null
+ || clientApplicationName.length() == 0) {
+ throw new IllegalArgumentException(
+ "Invalid clientApplicationName: " + clientApplicationName);
+ }
+
+ // create logger
+ logger = Logger.getLogger("client." + clientApplicationName);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "ClientApplicationImpl",
+ new Object[] { network, clientApplicationName, targetName,
+ targetPort, socksPort });
+
+ // check parameters
+ if (network == null || targetName == null || targetName.length() == 0
+ || targetPort < 0 || targetPort > 65535 || socksPort < 0
+ || socksPort > 65535) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "ClientApplicationImpl",
+ e);
+ throw e;
+ }
+
+ // remember parameters
+ this.clientApplicationName = clientApplicationName;
+ this.targetName = targetName;
+ this.targetPort = targetPort;
+ this.socksPort = socksPort;
+
+ // obtain and store reference on event manager
+ eventManager = network.getEventManagerImpl();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+ }
+
+ public synchronized void startRequests(final int retries,
+ final long timeoutForEachRetry, final boolean stopOnSuccess) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "performRequest",
+ new Object[] { retries, timeoutForEachRetry, stopOnSuccess });
+
+ // check parameters
+ if (retries <= 0 || timeoutForEachRetry < 0) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "performRequest", e);
+ throw e;
+ }
+
+ // check if we already have started a request (TODO change this to allow
+ // multiple requests in parallel? would be possible)
+ if (clientThread != null) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "Another request has already been started!");
+ logger.throwing(this.getClass().getName(), "performRequest", e);
+ throw e;
+ }
+
+ // create a thread that performs requests in the background
+ clientThread =
+ new RequestThread(retries, timeoutForEachRetry, stopOnSuccess);
+ clientThread.setName("Request Thread");
+ clientThread.setDaemon(true);
+ clientThread.start();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "performRequest");
+ }
+
+ public synchronized void stopRequest() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopRequest");
+
+ // check if a request is running
+ if (clientThread == null) {
+ final IllegalStateException e =
+ new IllegalStateException("Cannot stop "
+ + "request, because no request has been started!");
+ logger.throwing(this.getClass().getName(), "stopRequest", e);
+ throw e;
+ }
+
+ // log this event
+ logger.log(Level.FINE, "Shutting down client");
+
+ // interrupt thread
+ clientThread.stopRequest();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopRequest");
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": clientApplicationName=\""
+ + clientApplicationName + "\", targetAddress=\"" + targetName
+ + "\", targetPort=" + targetPort + ", socksPort=" + socksPort;
+ }
+
+ public String getClientApplicationName() {
+ return clientApplicationName;
+ }
+
+ public int getSocksPort() {
+ return socksPort;
+ }
+
+ public String getTargetName() {
+ return targetName;
+ }
+
+ public int getTargetPort() {
+ return targetPort;
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
old mode 100755
new mode 100644
index a8552bd..180dc1d
--- a/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
@@ -1,457 +1,457 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.logging.Level;
-
-import de.uniba.wiai.lspi.puppetor.DirectoryNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-
-/**
- * Implementation of <code>DirectoryNode</code>.
- *
- * @author kloesing
- */
-public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
-
- /**
- * Executable file for generating v3 directory authority certificates.
- *
- * TODO make this configurable!
- */
- protected static final File torGencertExecutable = new File("tor-gencert");
-
- /**
- * Internal thread class that is used to generate v3 directory authority
- * certificates in parallel, which can take a few seconds.
- */
- private class GenerateCertificateThread extends Thread {
-
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- // run tor-gencert
- final ProcessBuilder processBuilder =
- new ProcessBuilder(torGencertExecutable.getPath(),
- "--create-identity-key"// );
- , "--passphrase-fd", "0");
- final File workingDirectory =
- new File(workingDir.getAbsolutePath() + File.separator
- + "keys" + File.separator);
-
- // create working directory
- workingDirectory.mkdirs();
-
- processBuilder.directory(workingDirectory);
- processBuilder.redirectErrorStream(true);
- Process tmpProcess = null;
- try {
- tmpProcess = processBuilder.start();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not start tor-gencert process for generating a "
- + "v3 directory authority certificate!",
- e);
- logger.log(Level.WARNING, "Could not start tor-gencert "
- + "process for generating a v3 directory authority "
- + "certificate!", ex);
- setCaughtException(ex);
- return;
- }
-
- final BufferedWriter writer =
- new BufferedWriter(new OutputStreamWriter(tmpProcess
- .getOutputStream()));
- try {
- writer.write("somepassword\n");
- writer.close();
- } catch (final IOException e1) {
- System.out.println("Exception at write! " + e1.getMessage());
- e1.printStackTrace();
- }
- // final InputStream read = tmpProcess.getErrorStream();
-
- final InputStream stderr = tmpProcess.getInputStream();
- final InputStreamReader isr = new InputStreamReader(stderr);
- final BufferedReader br = new BufferedReader(isr);
- String line = null;
- try {
- while ((line = br.readLine()) != null) {
- ;
- }
- } catch (final IOException e1) {
- e1.printStackTrace();
- }
-
- // wait for process to terminate
- int exitValue = 0;
- try {
- exitValue = tmpProcess.waitFor();
- } catch (final InterruptedException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Interrupted while waiting for tor-gencert process to exit!",
- e);
- logger.log(Level.WARNING,
- "tor-gencert process was interrupted!", ex);
- setCaughtException(ex);
- return;
- }
-
- if (exitValue != 0) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not start tor-gencert process! tor-gencert exited with "
- + "exit value " + exitValue + "!");
- logger.log(Level.WARNING,
- "Could not start tor-gencert process!", ex);
- setCaughtException(ex);
- return;
- }
-
- // read fingerprint from file
- final File authorityCertificateFile =
- new File(workingDirectory.getAbsolutePath()
- + File.separator + "authority_certificate");
- String identity;
- try {
- final BufferedReader br2 =
- new BufferedReader(new FileReader(
- authorityCertificateFile));
- while ((line = br2.readLine()) != null
- && !line.startsWith("fingerprint ")) {
- ;
- }
- if (line == null) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not find fingerprint line in file "
- + "authority_certificate!");
- logger.log(Level.WARNING,
- "Could not find fingerprint line in file "
- + "authority_certificate!", ex);
- setCaughtException(ex);
- return;
- }
- identity = line.substring(line.indexOf(" ") + 1);
- br2.close();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not read fingerprint from file!", e);
- logger.log(Level.WARNING, "Could not read fingerprint file!",
- ex);
- setCaughtException(ex);
- return;
- }
-
- setV3Identity(identity);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- }
-
- /**
- * Set of routers that are approved by this directory node.
- */
- private final SortedSet<String> approvedRouters;
-
- /**
- * Creates a <code>DirectoryNodeImpl</code> and adds it to the given
- * <code>network</code>, but does not yet write its configuration to disk
- * or start the corresponding Tor process.
- *
- * @param network
- * Network configuration to which this node belongs.
- * @param nodeName
- * The name of the new node which may only consist of between 1
- * and 19 alpha-numeric characters.
- * @param controlPort
- * Port on which the Tor node will be listening for us as its
- * controller. May not be negative or greater than 65535.
- * @param socksPort
- * Port on which the Tor node will be listening for SOCKS
- * connection requests. May not be negative or greater than
- * 65535.
- * @param orPort
- * Port on which the Tor node will be listening for onion
- * requests by other Tor nodes. May not be negative or greater
- * than 65535.
- * @param dirPort
- * Port on which the Tor node will be listening for directory
- * requests from other Tor nodes. May not be negative or greater
- * than 65535.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @throws IllegalArgumentException
- * If at least one of the parameters is <code>null</code> or
- * has an invalid value.
- * @throws PuppeTorException
- * Thrown if an I/O problem occurs while writing the temporary
- * <code>approved-routers</code> file.
- */
- DirectoryNodeImpl(final NetworkImpl network, final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort, final String serverIpAddress) {
- // create superclass instance; parameter checking is done in super
- // constructor
- super(network, nodeName, controlPort, socksPort, orPort, dirPort,
- serverIpAddress);
-
- // log entering
- logger.entering(this.getClass().getName(), "DirectoryNodeImpl",
- new Object[] { network, nodeName, controlPort, socksPort,
- orPort, dirPort });
-
- // initialize attribute
- approvedRouters = new TreeSet<String>();
-
- // extend configuration by template configuration of directory nodes
- configuration.addAll(templateConfiguration);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "DirectoryNodeImpl");
- }
-
- /**
- * Invoked by the certificate generating thread: sets the generated v3
- * identity string.
- *
- * @param v3Identity
- * The generated v3 identity string.
- */
- private synchronized void setV3Identity(final String v3Identity) {
- // log entering
- logger.entering(this.getClass().getName(), "setV3Identity", v3Identity);
-
- // remember fingerprint and notify all waiting threads
- this.v3Identity = v3Identity;
- notifyAll();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "setV3Identity");
- }
-
- /**
- * The generated v3 identity string.
- */
- private String v3Identity;
-
- public synchronized String getV3Identity() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "getV3Identity");
-
- // wait until either the v3 identity has been determined or an exception
- // was caught
- while (v3Identity == null && caughtException == null) {
-
- try {
- wait(500);
- } catch (final InterruptedException e) {
- // do nothing
- }
- }
-
- if (caughtException != null) {
- logger.throwing(this.getClass().getName(), "getV3Identity",
- caughtException);
- throw caughtException;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "getV3Identity", v3Identity);
- return v3Identity;
- }
-
- public synchronized String getDirServerString() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "getDirServerString");
-
- // determine fingerprint
- String fingerprint = getFingerprint();
-
- // cut off router nickname
- fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
-
- // determine v3 identity
- final String determinedV3Identity = getV3Identity();
-
- // put everything together
- final String dirServerString =
- "DirServer " + nodeName + " v3ident=" + determinedV3Identity
- + " orport=" + orPort + " " + serverIpAddress + ":"
- + dirPort + " " + fingerprint;
-
- // log exiting and return dir server string
- logger.exiting(this.getClass().getName(), "getDirServerString",
- dirServerString);
- return dirServerString;
- }
-
- public void addApprovedRouters(final Set<String> routers) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addApprovedRouters",
- routers);
-
- // check parameter
- if (routers == null) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "addApprovedRouters", e);
- throw e;
- }
-
- // add the given approved router strings to the sorted set of already
- // known strings (if any)
- approvedRouters.addAll(routers);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "addApprovedRouters");
- }
-
- @Override
- protected synchronized void determineFingerprint() {
-
- // log entering
- logger.entering(this.getClass().getName(), "determineFingerprint");
-
- // start a thread to generate the directory's certificate
- final GenerateCertificateThread certificateThread =
- new GenerateCertificateThread();
- certificateThread.setName(nodeName + " Certificate Generator");
- certificateThread.start();
-
- // wait (non-blocking) for the v3 identity string
- try {
- getV3Identity();
- } catch (final PuppeTorException e1) {
- final PuppeTorException ex =
- new PuppeTorException("Could not read v3 identity string!",
- e1);
- caughtException = ex;
- return;
- }
-
- // create an empty approved-routers file to make Tor happy
- try {
- new File(workingDir.getAbsolutePath() + File.separator
- + "approved-routers").createNewFile();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not write empty approved-routers file!", e);
- caughtException = ex;
- return;
- }
-
- // invoke overwritten method
- super.determineFingerprint();
- }
-
- @Override
- public synchronized void writeConfiguration() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "writeConfiguration");
-
- // write approved-routers file
- try {
- final File approvedRoutersFile =
- new File(workingDir.getAbsolutePath() + File.separator
- + "approved-routers");
- final BufferedWriter bw =
- new BufferedWriter(new FileWriter(approvedRoutersFile));
- for (final String approvedRouter : approvedRouters) {
- bw.write(approvedRouter + "\n");
- }
- bw.close();
- } catch (final IOException e) {
- final PuppeTorException ex = new PuppeTorException(e);
- logger
- .throwing(this.getClass().getName(), "writeConfiguration",
- ex);
- throw ex;
- }
-
- // invoke overridden method
- super.writeConfiguration();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "writeConfiguration");
- }
-
- /**
- * Template configuration of directory nodes.
- */
- static List<String> templateConfiguration;
-
- static {
- templateConfiguration = new ArrayList<String>();
-
- // configure this node as an authoritative directory
- templateConfiguration.add("AuthoritativeDirectory 1");
- templateConfiguration.add("V2AuthoritativeDirectory 1");
- templateConfiguration.add("V3AuthoritativeDirectory 1");
- templateConfiguration.add("DirAllowPrivateAddresses 1");
- templateConfiguration.add("MinUptimeHidServDirectoryV2 0 minutes");
-
- // TODO This is now contained in proposal 135.
- // templateConfiguration.add("AuthDirMaxServersPerAddr 0");
- // templateConfiguration.add("AuthDirMaxServersPerAuthAddr 0");
- // templateConfiguration.add("V3AuthVotingInterval 5 minutes");
- // templateConfiguration.add("V3AuthVoteDelay 20 seconds");
- // templateConfiguration.add("V3AuthDistDelay 20 seconds");
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.logging.Level;
+
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Implementation of <code>DirectoryNode</code>.
+ *
+ * @author kloesing
+ */
+public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
+
+ /**
+ * Executable file for generating v3 directory authority certificates.
+ *
+ * TODO make this configurable!
+ */
+ protected static final File torGencertExecutable = new File("tor-gencert");
+
+ /**
+ * Internal thread class that is used to generate v3 directory authority
+ * certificates in parallel, which can take a few seconds.
+ */
+ private class GenerateCertificateThread extends Thread {
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // run tor-gencert
+ final ProcessBuilder processBuilder =
+ new ProcessBuilder(torGencertExecutable.getPath(),
+ "--create-identity-key"// );
+ , "--passphrase-fd", "0");
+ final File workingDirectory =
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "keys" + File.separator);
+
+ // create working directory
+ workingDirectory.mkdirs();
+
+ processBuilder.directory(workingDirectory);
+ processBuilder.redirectErrorStream(true);
+ Process tmpProcess = null;
+ try {
+ tmpProcess = processBuilder.start();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not start tor-gencert process for generating a "
+ + "v3 directory authority certificate!",
+ e);
+ logger.log(Level.WARNING, "Could not start tor-gencert "
+ + "process for generating a v3 directory authority "
+ + "certificate!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ final BufferedWriter writer =
+ new BufferedWriter(new OutputStreamWriter(tmpProcess
+ .getOutputStream()));
+ try {
+ writer.write("somepassword\n");
+ writer.close();
+ } catch (final IOException e1) {
+ System.out.println("Exception at write! " + e1.getMessage());
+ e1.printStackTrace();
+ }
+ // final InputStream read = tmpProcess.getErrorStream();
+
+ final InputStream stderr = tmpProcess.getInputStream();
+ final InputStreamReader isr = new InputStreamReader(stderr);
+ final BufferedReader br = new BufferedReader(isr);
+ String line = null;
+ try {
+ while ((line = br.readLine()) != null) {
+ ;
+ }
+ } catch (final IOException e1) {
+ e1.printStackTrace();
+ }
+
+ // wait for process to terminate
+ int exitValue = 0;
+ try {
+ exitValue = tmpProcess.waitFor();
+ } catch (final InterruptedException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Interrupted while waiting for tor-gencert process to exit!",
+ e);
+ logger.log(Level.WARNING,
+ "tor-gencert process was interrupted!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ if (exitValue != 0) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not start tor-gencert process! tor-gencert exited with "
+ + "exit value " + exitValue + "!");
+ logger.log(Level.WARNING,
+ "Could not start tor-gencert process!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ // read fingerprint from file
+ final File authorityCertificateFile =
+ new File(workingDirectory.getAbsolutePath()
+ + File.separator + "authority_certificate");
+ String identity;
+ try {
+ final BufferedReader br2 =
+ new BufferedReader(new FileReader(
+ authorityCertificateFile));
+ while ((line = br2.readLine()) != null
+ && !line.startsWith("fingerprint ")) {
+ ;
+ }
+ if (line == null) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not find fingerprint line in file "
+ + "authority_certificate!");
+ logger.log(Level.WARNING,
+ "Could not find fingerprint line in file "
+ + "authority_certificate!", ex);
+ setCaughtException(ex);
+ return;
+ }
+ identity = line.substring(line.indexOf(" ") + 1);
+ br2.close();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not read fingerprint from file!", e);
+ logger.log(Level.WARNING, "Could not read fingerprint file!",
+ ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ setV3Identity(identity);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * Set of routers that are approved by this directory node.
+ */
+ private final SortedSet<String> approvedRouters;
+
+ /**
+ * Creates a <code>DirectoryNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The name of the new node which may only consist of between 1
+ * and 19 alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
+ * @param orPort
+ * Port on which the Tor node will be listening for onion
+ * requests by other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param dirPort
+ * Port on which the Tor node will be listening for directory
+ * requests from other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ * @throws PuppeTorException
+ * Thrown if an I/O problem occurs while writing the temporary
+ * <code>approved-routers</code> file.
+ */
+ DirectoryNodeImpl(final NetworkImpl network, final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort, final String serverIpAddress) {
+ // create superclass instance; parameter checking is done in super
+ // constructor
+ super(network, nodeName, controlPort, socksPort, orPort, dirPort,
+ serverIpAddress);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "DirectoryNodeImpl",
+ new Object[] { network, nodeName, controlPort, socksPort,
+ orPort, dirPort });
+
+ // initialize attribute
+ approvedRouters = new TreeSet<String>();
+
+ // extend configuration by template configuration of directory nodes
+ configuration.addAll(templateConfiguration);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "DirectoryNodeImpl");
+ }
+
+ /**
+ * Invoked by the certificate generating thread: sets the generated v3
+ * identity string.
+ *
+ * @param v3Identity
+ * The generated v3 identity string.
+ */
+ private synchronized void setV3Identity(final String v3Identity) {
+ // log entering
+ logger.entering(this.getClass().getName(), "setV3Identity", v3Identity);
+
+ // remember fingerprint and notify all waiting threads
+ this.v3Identity = v3Identity;
+ notifyAll();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "setV3Identity");
+ }
+
+ /**
+ * The generated v3 identity string.
+ */
+ private String v3Identity;
+
+ public synchronized String getV3Identity() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "getV3Identity");
+
+ // wait until either the v3 identity has been determined or an exception
+ // was caught
+ while (v3Identity == null && caughtException == null) {
+
+ try {
+ wait(500);
+ } catch (final InterruptedException e) {
+ // do nothing
+ }
+ }
+
+ if (caughtException != null) {
+ logger.throwing(this.getClass().getName(), "getV3Identity",
+ caughtException);
+ throw caughtException;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "getV3Identity", v3Identity);
+ return v3Identity;
+ }
+
+ public synchronized String getDirServerString() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "getDirServerString");
+
+ // determine fingerprint
+ String fingerprint = getFingerprint();
+
+ // cut off router nickname
+ fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
+
+ // determine v3 identity
+ final String determinedV3Identity = getV3Identity();
+
+ // put everything together
+ final String dirServerString =
+ "DirServer " + nodeName + " v3ident=" + determinedV3Identity
+ + " orport=" + orPort + " " + serverIpAddress + ":"
+ + dirPort + " " + fingerprint;
+
+ // log exiting and return dir server string
+ logger.exiting(this.getClass().getName(), "getDirServerString",
+ dirServerString);
+ return dirServerString;
+ }
+
+ public void addApprovedRouters(final Set<String> routers) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addApprovedRouters",
+ routers);
+
+ // check parameter
+ if (routers == null) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "addApprovedRouters", e);
+ throw e;
+ }
+
+ // add the given approved router strings to the sorted set of already
+ // known strings (if any)
+ approvedRouters.addAll(routers);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "addApprovedRouters");
+ }
+
+ @Override
+ protected synchronized void determineFingerprint() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "determineFingerprint");
+
+ // start a thread to generate the directory's certificate
+ final GenerateCertificateThread certificateThread =
+ new GenerateCertificateThread();
+ certificateThread.setName(nodeName + " Certificate Generator");
+ certificateThread.start();
+
+ // wait (non-blocking) for the v3 identity string
+ try {
+ getV3Identity();
+ } catch (final PuppeTorException e1) {
+ final PuppeTorException ex =
+ new PuppeTorException("Could not read v3 identity string!",
+ e1);
+ caughtException = ex;
+ return;
+ }
+
+ // create an empty approved-routers file to make Tor happy
+ try {
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "approved-routers").createNewFile();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not write empty approved-routers file!", e);
+ caughtException = ex;
+ return;
+ }
+
+ // invoke overwritten method
+ super.determineFingerprint();
+ }
+
+ @Override
+ public synchronized void writeConfiguration() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "writeConfiguration");
+
+ // write approved-routers file
+ try {
+ final File approvedRoutersFile =
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "approved-routers");
+ final BufferedWriter bw =
+ new BufferedWriter(new FileWriter(approvedRoutersFile));
+ for (final String approvedRouter : approvedRouters) {
+ bw.write(approvedRouter + "\n");
+ }
+ bw.close();
+ } catch (final IOException e) {
+ final PuppeTorException ex = new PuppeTorException(e);
+ logger
+ .throwing(this.getClass().getName(), "writeConfiguration",
+ ex);
+ throw ex;
+ }
+
+ // invoke overridden method
+ super.writeConfiguration();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "writeConfiguration");
+ }
+
+ /**
+ * Template configuration of directory nodes.
+ */
+ static List<String> templateConfiguration;
+
+ static {
+ templateConfiguration = new ArrayList<String>();
+
+ // configure this node as an authoritative directory
+ templateConfiguration.add("AuthoritativeDirectory 1");
+ templateConfiguration.add("V2AuthoritativeDirectory 1");
+ templateConfiguration.add("V3AuthoritativeDirectory 1");
+ templateConfiguration.add("DirAllowPrivateAddresses 1");
+ templateConfiguration.add("MinUptimeHidServDirectoryV2 0 minutes");
+
+ // TODO This is now contained in proposal 135.
+ // templateConfiguration.add("AuthDirMaxServersPerAddr 0");
+ // templateConfiguration.add("AuthDirMaxServersPerAuthAddr 0");
+ // templateConfiguration.add("V3AuthVotingInterval 5 minutes");
+ // templateConfiguration.add("V3AuthVoteDelay 20 seconds");
+ // templateConfiguration.add("V3AuthDistDelay 20 seconds");
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
old mode 100755
new mode 100644
index 79c2c2b..26cbe00
--- a/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
@@ -1,755 +1,755 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.EventType;
-import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
-import de.uniba.wiai.lspi.puppetor.NodeEventType;
-
-/**
- * Implementation of <code>EventManager</code>.
- *
- * @author kloesing
- */
-public class EventManagerImpl implements EventManager {
-
- /**
- * Registered event handlers for specific sources.
- */
- private final Map<String, Set<EventListener>> eventHandlers;
-
- /**
- * Registered event handlers for all sources.
- */
- private final Set<EventListener> eventHandlersForAllSources;
-
- /**
- * Logger for this event manager which is called "event." plus the name of
- * the network.
- */
- private final Logger logger;
-
- /**
- * Events observed so far.
- */
- private final Map<String, List<Event>> observedEvents;
-
- /**
- * Set of all registered event sources. This is required to ensure that
- * requests for events from a given source specify valid event sources.
- */
- private final Set<String> eventSources;
-
- /**
- * Creates a new <code>EventManagerImpl</code> for the network with name
- * <code>networkName</code> and initializes it.
- *
- * @param networkName
- * Name of this event manager that is used as part of the logger
- * name.
- * @throws IllegalArgumentException
- * Thrown if the given <code>networkName</code> is either
- * <code>null</code> or a zero-length string.
- */
- EventManagerImpl(final String networkName) {
-
- // check if networkName can be used as logger name
- if (networkName == null || networkName.length() == 0) {
- throw new IllegalArgumentException("Invalid networkName: "
- + networkName);
- }
-
- // create logger
- logger = Logger.getLogger("event." + networkName);
-
- // log entering
- logger.entering(this.getClass().getName(), "EventManagerImpl",
- networkName);
-
- // create data structures
- observedEvents = new HashMap<String, List<Event>>();
- eventHandlers = new HashMap<String, Set<EventListener>>();
- eventHandlersForAllSources = new HashSet<EventListener>();
- eventSources = new HashSet<String>();
-
- // start thread to parse events
- final Thread eventParseThread = new Thread() {
- @Override
- public void run() {
- while (true) {
- parseNextEvent();
- }
- }
- };
- eventParseThread.setDaemon(true);
- eventParseThread.start();
-
- // initialize event type patterns
- initializeEventTypePatterns();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "EventManagerImpl");
- }
-
- public synchronized List<Event> addEventListener(final String source,
- final EventListener listener) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addEventListener",
- new Object[] { source, listener });
-
- // check parameters
- if (source == null || listener == null
- || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "addEventListener", e);
- throw e;
- }
-
- // if necessary, create new event listener set for source
- if (!eventHandlers.containsKey(source)) {
- eventHandlers.put(source, new HashSet<EventListener>());
- }
-
- // add listener
- eventHandlers.get(source).add(listener);
-
- // log change
- logger.log(Level.FINE, "Added event listener for source " + source);
-
- // log exiting and return
- final List<Event> result = getEventHistory(source);
- logger.exiting(this.getClass().getName(), "addEventListener", result);
- return result;
- }
-
- public synchronized void addEventListener(final EventListener listener) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addEventListener",
- new Object[] { listener });
-
- // check parameters
- if (listener == null) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "addEventListener", e);
- throw e;
- }
-
- // add listener
- eventHandlersForAllSources.add(listener);
-
- // log change
- logger.log(Level.FINE, "Added event listener for all sources.");
-
- // log exiting and return
- logger.exiting(this.getClass().getName(), "addEventListener");
- return;
- }
-
- public synchronized List<Event> getEventHistory(final String source) {
-
- // log entering
- logger.entering(this.getClass().getName(), "getEventHistory", source);
-
- // check parameter
- if (source == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "getEventHistory", e);
- throw e;
- }
-
- // prepare result
- final List<Event> result = new ArrayList<Event>();
-
- // did we already observe events for this source?
- if (observedEvents.containsKey(source)) {
- // yes, add all events to result list
- result.addAll(observedEvents.get(source));
- }
-
- // log exiting and return result
- logger.exiting(this.getClass().getName(), "getEventHistory", result);
- return result;
- }
-
- public synchronized boolean hasEventOccured(final String source,
- final EventType type) {
-
- // log entering
- logger.entering(this.getClass().getName(), "hasEventOccured",
- new Object[] { source, type });
-
- // check parameters
- if (source == null || type == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "hasEventOccured", e);
- throw e;
- }
-
- // determine result
- boolean result = false;
- if (observedEvents.containsKey(source)) {
- for (final Event event : observedEvents.get(source)) {
- if (event.getType() == type) {
- result = true;
- break;
- }
- }
- }
-
- // log exiting and return result
- logger.exiting(this.getClass().getName(), "hasEventOccured", result);
- return result;
- }
-
- /**
- * An ordered list of all log statements that are still unparsed.
- */
- private final List<EventImpl> unparsedLogStatements =
- new LinkedList<EventImpl>();
-
- /**
- * Stores the occurrence of an unparsed Tor log events that might result in
- * an event. All such unparsed events are later parsed by a background
- * thread in invocation order. Then, the occurrence time and the event type
- * will be parsed from the log message; if the log message does not contain
- * anything of interest, the event will be discarded.
- *
- * @param source
- * The event source.
- * @param logMessage
- * The log message.
- * @throws IllegalArgumentException
- * Thrown if the source is unknown.
- */
- synchronized void observeUnparsedEvent(final String source,
- final String logMessage) {
- unparsedLogStatements.add(new EventImpl(source, logMessage));
- notifyAll();
- }
-
- /**
- * Add an internal event to the event queue.
- *
- * @param occurrenceTime
- * The occurrence time of the event.
- * @param source
- * The event source.
- * @param type
- * The event type.
- * @param message
- * The event message.
- * @throws IllegalArgumentException
- * Thrown if the source is unknown.
- */
- public synchronized void observeInternalEvent(final long occurrenceTime,
- final String source, final EventType type, final String message) {
-
- if (!eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "observeInternalEvent",
- e);
- throw e;
- }
-
- unparsedLogStatements.add(new EventImpl(occurrenceTime, source, type,
- message));
- notifyAll();
- }
-
- /**
- * Parses a log statement coming from Tor and decides whether it is
- * interesting for us.
- */
- void parseNextEvent() {
-
- // wait for the next event in the queue
- EventImpl event = null;
- synchronized (this) {
- while (unparsedLogStatements.isEmpty()) {
- try {
- wait();
- } catch (final InterruptedException e) {}
- }
- event = unparsedLogStatements.remove(0);
- }
-
- // does the event contain a known source? if not, discard it
- if (!eventSources.contains(event.getSource())) {
- logger.log(Level.WARNING,
- "Unknown event source while parsing an event: "
- + event.getSource());
- return;
- }
-
- // does the event require parsing? if not, process immediately
- if (event.getType() != null) {
- observeEvent(event);
- } else {
- final String line = event.getMessage();
-
- /*
- * the logging output of Tor does not contain a year component; put
- * in the year at the time of parsing (which happens approx. 10 ms
- * after the logging took place) in the good hope that this tool is
- * not run at midnight on New Year's Eve...
- */
- final Calendar c = Calendar.getInstance();
- final int currentYear = c.get(Calendar.YEAR);
-
- // try to apply one of the event type patterns
- for (final Entry<Pattern, EventType> entry : eventTypePatterns
- .entrySet()) {
- final Matcher matcher = entry.getKey().matcher(line);
- if (matcher.find()) {
- final SimpleDateFormat sdf =
- new SimpleDateFormat("MMM dd HH:mm:ss.SSS",
- Locale.US);
- final Date logTime = sdf.parse(line, new ParsePosition(0));
- c.setTimeInMillis(logTime.getTime());
- c.set(Calendar.YEAR, currentYear);
- final long t = c.getTimeInMillis();
- event.setOccurenceTime(t);
- event.setType(entry.getValue());
- observeEvent(event);
- break;
- }
- }
- }
- }
-
- /**
- * Map of all patterns, that should be included when parsing log statements
- * coming from Tor, and the respective event types of the events that should
- * be fired.
- */
- Map<Pattern, EventType> eventTypePatterns;
-
- public synchronized void registerEventTypePattern(
- final String patternString, final EventType eventType) {
- eventTypePatterns.put(Pattern.compile(patternString), eventType);
- }
-
- /**
- * Initializes the parsing engine with the standard log message patterns
- * that should be included in current Tor. Any further patterns need to be
- * added by the test application manually.
- */
- private void initializeEventTypePatterns() {
- eventTypePatterns = new HashMap<Pattern, EventType>();
- registerEventTypePattern("Opening Control listener on .*",
- NodeEventType.NODE_CONTROL_PORT_OPENED);
- registerEventTypePattern("Tor has successfully opened a circuit. "
- + "Looks like client functionality is working.",
- NodeEventType.NODE_CIRCUIT_OPENED);
- registerEventTypePattern(
- "Established circuit .* as introduction "
- + "point for service .*",
- HiddenServiceEventType.BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO);
- registerEventTypePattern("Received INTRO_ESTABLISHED cell on "
- + "circuit .* for service .*",
- HiddenServiceEventType.BOB_INTRO_ESTABLISHED_RECEIVED);
- registerEventTypePattern("Sending publish request for hidden "
- + "service .*", HiddenServiceEventType.BOB_SENDING_PUBLISH_DESC);
- registerEventTypePattern("Uploaded rendezvous descriptor",
- HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED);
- registerEventTypePattern("Received INTRODUCE2 cell for service .* "
- + "on circ .*", HiddenServiceEventType.BOB_INTRODUCE2_RECEIVED);
- registerEventTypePattern("Done building circuit .* to rendezvous "
- + "with cookie .* for service .*",
- HiddenServiceEventType.BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1);
- registerEventTypePattern("begin is for rendezvous",
- HiddenServiceEventType.BOB_APP_CONN_OPENED);
- registerEventTypePattern("Got a hidden service request for ID '.*'",
- HiddenServiceEventType.ALICE_ONION_REQUEST_RECEIVED);
- registerEventTypePattern("Fetching rendezvous descriptor for "
- + "service .*", HiddenServiceEventType.ALICE_SENDING_FETCH_DESC);
- registerEventTypePattern("Received rendezvous descriptor",
- HiddenServiceEventType.ALICE_DESC_FETCHED_RECEIVED);
- registerEventTypePattern(
- "Sending an ESTABLISH_RENDEZVOUS cell",
- HiddenServiceEventType.ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS);
- registerEventTypePattern("Got rendezvous ack. This circuit is now "
- + "ready for rendezvous",
- HiddenServiceEventType.ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED);
- registerEventTypePattern("introcirc is open",
- HiddenServiceEventType.ALICE_BUILT_INTRO_CIRC);
- registerEventTypePattern("Sending an INTRODUCE1 cell",
- HiddenServiceEventType.ALICE_SENDING_INTRODUCE1);
- registerEventTypePattern("Received ack. Telling rend circ",
- HiddenServiceEventType.ALICE_INTRODUCE_ACK_RECEIVED);
- registerEventTypePattern(
- "Got RENDEZVOUS2 cell from hidden service",
- HiddenServiceEventType.ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED);
- registerEventTypePattern("Handling rendezvous descriptor post",
- HiddenServiceEventType.DIR_PUBLISH_DESC_RECEIVED);
- registerEventTypePattern("Handling rendezvous descriptor get",
- HiddenServiceEventType.DIR_FETCH_DESC_RECEIVED);
- registerEventTypePattern(
- "Received an ESTABLISH_INTRO request on circuit .*",
- HiddenServiceEventType.IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED);
- registerEventTypePattern(
- "Received an INTRODUCE1 request on circuit .*",
- HiddenServiceEventType.IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK);
- registerEventTypePattern(
- "Received an ESTABLISH_RENDEZVOUS request on circuit .*",
- HiddenServiceEventType.RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED);
- registerEventTypePattern(
- "Got request for rendezvous from circuit .* to cookie .*",
- HiddenServiceEventType.RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2);
- }
-
- /**
- * Stores the given <code>event</code> from <code>source</code> to the
- * event history and propagates its occurrence to all registered event
- * handlers.
- *
- * @param event
- * The observed event.
- */
- private synchronized void observeEvent(final Event event) {
-
- // log entering
- logger.entering(this.getClass().getName(), "observeEvent", event);
-
- final String source = event.getSource();
- logger.log(Level.FINE, "Observed event " + event + " from source "
- + source + "!");
-
- // remember observed event
- if (!observedEvents.containsKey(event.getSource())) {
- observedEvents.put(source, new ArrayList<Event>());
- }
- observedEvents.get(source).add(event);
-
- // notify waiting threads
- notifyAll();
-
- // inform event listeners
- if (eventHandlers.containsKey(source)) {
-
- // make a copy of the event handler set, because some event handlers
- // might want to remove themselves from this set while handling the
- // event
- final Set<EventListener> copyOfEventHandlers =
- new HashSet<EventListener>(eventHandlers.get(source));
-
- for (final EventListener eventHandler : copyOfEventHandlers) {
-
- logger.log(Level.FINE, "Informing event listener "
- + eventHandler + " about recently observed event "
- + event + " from source " + source + "!");
- eventHandler.handleEvent(event);
-
- }
- }
-
- // make a copy of the event handler set for all sources, because some
- // event handlers might want to remove themselves from this set while
- // handling the event
- final Set<EventListener> copyOfEventHandlersForAllSources =
- new HashSet<EventListener>(eventHandlersForAllSources);
-
- for (final EventListener eventHandler : copyOfEventHandlersForAllSources) {
-
- logger.log(Level.FINE, "Informing event listener " + eventHandler
- + " about recently observed event " + event
- + " from source " + source + "!");
- eventHandler.handleEvent(event);
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "observeEvent");
- }
-
- public synchronized void removeEventListener(
- final EventListener eventListener) {
-
- // log entering
- logger.entering(this.getClass().getName(), "removeEventListener",
- eventListener);
-
- // check parameters
- if (eventListener == null) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger
- .throwing(this.getClass().getName(), "removeEventListener",
- e);
- throw e;
- }
-
- // don't know to which source this listener has been added (may to more
- // than one), so remove it from all possible sets
- for (final Set<EventListener> set : eventHandlers.values()) {
- if (set.remove(eventListener)) {
- logger.log(Level.FINE, "Removed event listener!");
- }
- }
-
- // remove from event listeners for all sources
- if (eventHandlersForAllSources.remove(eventListener)) {
- logger.log(Level.FINE, "Removed event listener!");
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "removeEventListener");
- }
-
- public synchronized void waitForAnyOccurence(final String source,
- final EventType event) {
-
- // log entering
- logger.entering(this.getClass().getName(), "waitForAnyOccurence",
- new Object[] { source, event });
-
- // check parameters
- if (source == null || event == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger
- .throwing(this.getClass().getName(), "waitForAnyOccurence",
- e);
- throw e;
- }
-
- // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
- // means to wait forever
- waitForAnyOccurence(source, event, -1L);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "waitForAnyOccurence");
-
- }
-
- public synchronized boolean waitForAnyOccurence(final String source,
- final EventType event, final long maximumTimeToWaitInMillis) {
-
- // log entering
- logger.entering(this.getClass().getName(), "waitForAnyOccurence",
- new Object[] { source, event, maximumTimeToWaitInMillis });
-
- // check parameters
- if (source == null || event == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger
- .throwing(this.getClass().getName(), "waitForAnyOccurence",
- e);
- throw e;
- }
-
- // check if we have already observed the event
- if (hasEventOccured(source, event)) {
- logger.log(Level.FINE, "Waiting for any occurence of event "
- + event + " returned immediately!");
- logger.exiting(this.getClass().getName(), "waitForAnyOccurence",
- true);
- return true;
- }
-
- // invoke method that waits for next occurence of the event
- final boolean result =
- waitForNextOccurence(source, event, maximumTimeToWaitInMillis);
-
- // log exiting and return result
- logger
- .exiting(this.getClass().getName(), "waitForAnyOccurence",
- result);
- return result;
- }
-
- public synchronized void waitForNextOccurence(final String source,
- final EventType event) {
-
- // log entering
- logger.entering(this.getClass().getName(), "waitForNextOccurence",
- new Object[] { source, event });
-
- // check parameters
- if (source == null || event == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "waitForNextOccurence",
- e);
- throw e;
- }
-
- // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
- // means to wait forever
- waitForNextOccurence(source, event, -1L);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "waitForNextOccurence");
- }
-
- public synchronized boolean waitForNextOccurence(final String source,
- final EventType type, final long maximumTimeToWaitInMillis) {
-
- // log entering
- logger.entering(this.getClass().getName(), "waitForNextOccurence",
- new Object[] { source, type, maximumTimeToWaitInMillis });
-
- // check parameters
- if (source == null || type == null || !eventSources.contains(source)) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "waitForNextOccurence",
- e);
- throw e;
- }
-
- // distinguish between negative waiting time (wait forever) and zero or
- // positive waiting time
- if (maximumTimeToWaitInMillis < 0) {
-
- // wait forever
- while (!hasEventOccured(source, type)) {
-
- logger.log(Level.FINEST,
- "We will wait infinetely for the next occurence of "
- + "event type " + type + " from source "
- + source + "...");
- try {
- wait();
- } catch (final InterruptedException e) {
- // don't handle
- }
-
- logger.log(Level.FINEST,
- "We have been notified about an observed event while "
- + "waiting for events of type " + type
- + " from source " + source
- + "; need to check whether the observed event "
- + "is what we are looking for...");
- }
-
- logger.log(Level.FINE, "Waiting for occurence of event type "
- + type + " succeeded!");
-
- // log exiting and return result
- logger.exiting(this.getClass().getName(), "waitForNextOccurence",
- true);
- return true;
-
- }
-
- // wait for the given time at most
- final long endOfTime =
- System.currentTimeMillis() + maximumTimeToWaitInMillis;
- long timeLeft = 0;
- while (!hasEventOccured(source, type)
- && (timeLeft = endOfTime - System.currentTimeMillis()) > 0) {
-
- logger.log(Level.FINEST, "We will wait for " + timeLeft
- + " milliseconds for the next occurence of event type "
- + type + " from source " + source + "...");
-
- try {
- wait(timeLeft);
- } catch (final InterruptedException e) {
- // don't handle
- }
-
- logger.log(Level.FINEST,
- "We have been notified about an observed event while "
- + "waiting for events of type " + type
- + " from source " + source
- + "; need to check whether the observed event "
- + "is what we are looking for...");
- }
-
- // determine result
- final boolean result = hasEventOccured(source, type);
-
- logger.log(Level.FINE, "Waiting for next occurence of event type "
- + type + " from source " + source
- + (result ? " succeeded!" : " did not succeed!"));
-
- // log exiting and return result
- logger.exiting(this.getClass().getName(), "waitForNextOccurence",
- result);
- return result;
- }
-
- /**
- * Adds the given <code>source</code> as possible event source.
- *
- * @param source
- * The name of the node, client, or server to add.
- * @throws IllegalArgumentException
- * Thrown if there is already an event source with this name.
- */
- void addEventSource(final String source) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addEventSource", source);
-
- // check if source name is unique in this network
- if (eventSources.contains(source)) {
- final IllegalArgumentException e =
- new IllegalArgumentException(
- "There is already an event source with name "
- + source + " in this network!");
- logger.throwing(this.getClass().getName(), "addEventSource", e);
- throw e;
- }
-
- // add event source name
- eventSources.add(source);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "addEventSource");
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName();
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.EventType;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
+import de.uniba.wiai.lspi.puppetor.NodeEventType;
+
+/**
+ * Implementation of <code>EventManager</code>.
+ *
+ * @author kloesing
+ */
+public class EventManagerImpl implements EventManager {
+
+ /**
+ * Registered event handlers for specific sources.
+ */
+ private final Map<String, Set<EventListener>> eventHandlers;
+
+ /**
+ * Registered event handlers for all sources.
+ */
+ private final Set<EventListener> eventHandlersForAllSources;
+
+ /**
+ * Logger for this event manager which is called "event." plus the name of
+ * the network.
+ */
+ private final Logger logger;
+
+ /**
+ * Events observed so far.
+ */
+ private final Map<String, List<Event>> observedEvents;
+
+ /**
+ * Set of all registered event sources. This is required to ensure that
+ * requests for events from a given source specify valid event sources.
+ */
+ private final Set<String> eventSources;
+
+ /**
+ * Creates a new <code>EventManagerImpl</code> for the network with name
+ * <code>networkName</code> and initializes it.
+ *
+ * @param networkName
+ * Name of this event manager that is used as part of the logger
+ * name.
+ * @throws IllegalArgumentException
+ * Thrown if the given <code>networkName</code> is either
+ * <code>null</code> or a zero-length string.
+ */
+ EventManagerImpl(final String networkName) {
+
+ // check if networkName can be used as logger name
+ if (networkName == null || networkName.length() == 0) {
+ throw new IllegalArgumentException("Invalid networkName: "
+ + networkName);
+ }
+
+ // create logger
+ logger = Logger.getLogger("event." + networkName);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "EventManagerImpl",
+ networkName);
+
+ // create data structures
+ observedEvents = new HashMap<String, List<Event>>();
+ eventHandlers = new HashMap<String, Set<EventListener>>();
+ eventHandlersForAllSources = new HashSet<EventListener>();
+ eventSources = new HashSet<String>();
+
+ // start thread to parse events
+ final Thread eventParseThread = new Thread() {
+ @Override
+ public void run() {
+ while (true) {
+ parseNextEvent();
+ }
+ }
+ };
+ eventParseThread.setDaemon(true);
+ eventParseThread.start();
+
+ // initialize event type patterns
+ initializeEventTypePatterns();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "EventManagerImpl");
+ }
+
+ public synchronized List<Event> addEventListener(final String source,
+ final EventListener listener) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addEventListener",
+ new Object[] { source, listener });
+
+ // check parameters
+ if (source == null || listener == null
+ || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "addEventListener", e);
+ throw e;
+ }
+
+ // if necessary, create new event listener set for source
+ if (!eventHandlers.containsKey(source)) {
+ eventHandlers.put(source, new HashSet<EventListener>());
+ }
+
+ // add listener
+ eventHandlers.get(source).add(listener);
+
+ // log change
+ logger.log(Level.FINE, "Added event listener for source " + source);
+
+ // log exiting and return
+ final List<Event> result = getEventHistory(source);
+ logger.exiting(this.getClass().getName(), "addEventListener", result);
+ return result;
+ }
+
+ public synchronized void addEventListener(final EventListener listener) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addEventListener",
+ new Object[] { listener });
+
+ // check parameters
+ if (listener == null) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "addEventListener", e);
+ throw e;
+ }
+
+ // add listener
+ eventHandlersForAllSources.add(listener);
+
+ // log change
+ logger.log(Level.FINE, "Added event listener for all sources.");
+
+ // log exiting and return
+ logger.exiting(this.getClass().getName(), "addEventListener");
+ return;
+ }
+
+ public synchronized List<Event> getEventHistory(final String source) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "getEventHistory", source);
+
+ // check parameter
+ if (source == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "getEventHistory", e);
+ throw e;
+ }
+
+ // prepare result
+ final List<Event> result = new ArrayList<Event>();
+
+ // did we already observe events for this source?
+ if (observedEvents.containsKey(source)) {
+ // yes, add all events to result list
+ result.addAll(observedEvents.get(source));
+ }
+
+ // log exiting and return result
+ logger.exiting(this.getClass().getName(), "getEventHistory", result);
+ return result;
+ }
+
+ public synchronized boolean hasEventOccured(final String source,
+ final EventType type) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "hasEventOccured",
+ new Object[] { source, type });
+
+ // check parameters
+ if (source == null || type == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "hasEventOccured", e);
+ throw e;
+ }
+
+ // determine result
+ boolean result = false;
+ if (observedEvents.containsKey(source)) {
+ for (final Event event : observedEvents.get(source)) {
+ if (event.getType() == type) {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ // log exiting and return result
+ logger.exiting(this.getClass().getName(), "hasEventOccured", result);
+ return result;
+ }
+
+ /**
+ * An ordered list of all log statements that are still unparsed.
+ */
+ private final List<EventImpl> unparsedLogStatements =
+ new LinkedList<EventImpl>();
+
+ /**
+ * Stores the occurrence of an unparsed Tor log events that might result in
+ * an event. All such unparsed events are later parsed by a background
+ * thread in invocation order. Then, the occurrence time and the event type
+ * will be parsed from the log message; if the log message does not contain
+ * anything of interest, the event will be discarded.
+ *
+ * @param source
+ * The event source.
+ * @param logMessage
+ * The log message.
+ * @throws IllegalArgumentException
+ * Thrown if the source is unknown.
+ */
+ synchronized void observeUnparsedEvent(final String source,
+ final String logMessage) {
+ unparsedLogStatements.add(new EventImpl(source, logMessage));
+ notifyAll();
+ }
+
+ /**
+ * Add an internal event to the event queue.
+ *
+ * @param occurrenceTime
+ * The occurrence time of the event.
+ * @param source
+ * The event source.
+ * @param type
+ * The event type.
+ * @param message
+ * The event message.
+ * @throws IllegalArgumentException
+ * Thrown if the source is unknown.
+ */
+ public synchronized void observeInternalEvent(final long occurrenceTime,
+ final String source, final EventType type, final String message) {
+
+ if (!eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "observeInternalEvent",
+ e);
+ throw e;
+ }
+
+ unparsedLogStatements.add(new EventImpl(occurrenceTime, source, type,
+ message));
+ notifyAll();
+ }
+
+ /**
+ * Parses a log statement coming from Tor and decides whether it is
+ * interesting for us.
+ */
+ void parseNextEvent() {
+
+ // wait for the next event in the queue
+ EventImpl event = null;
+ synchronized (this) {
+ while (unparsedLogStatements.isEmpty()) {
+ try {
+ wait();
+ } catch (final InterruptedException e) {}
+ }
+ event = unparsedLogStatements.remove(0);
+ }
+
+ // does the event contain a known source? if not, discard it
+ if (!eventSources.contains(event.getSource())) {
+ logger.log(Level.WARNING,
+ "Unknown event source while parsing an event: "
+ + event.getSource());
+ return;
+ }
+
+ // does the event require parsing? if not, process immediately
+ if (event.getType() != null) {
+ observeEvent(event);
+ } else {
+ final String line = event.getMessage();
+
+ /*
+ * the logging output of Tor does not contain a year component; put
+ * in the year at the time of parsing (which happens approx. 10 ms
+ * after the logging took place) in the good hope that this tool is
+ * not run at midnight on New Year's Eve...
+ */
+ final Calendar c = Calendar.getInstance();
+ final int currentYear = c.get(Calendar.YEAR);
+
+ // try to apply one of the event type patterns
+ for (final Entry<Pattern, EventType> entry : eventTypePatterns
+ .entrySet()) {
+ final Matcher matcher = entry.getKey().matcher(line);
+ if (matcher.find()) {
+ final SimpleDateFormat sdf =
+ new SimpleDateFormat("MMM dd HH:mm:ss.SSS",
+ Locale.US);
+ final Date logTime = sdf.parse(line, new ParsePosition(0));
+ c.setTimeInMillis(logTime.getTime());
+ c.set(Calendar.YEAR, currentYear);
+ final long t = c.getTimeInMillis();
+ event.setOccurenceTime(t);
+ event.setType(entry.getValue());
+ observeEvent(event);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Map of all patterns, that should be included when parsing log statements
+ * coming from Tor, and the respective event types of the events that should
+ * be fired.
+ */
+ Map<Pattern, EventType> eventTypePatterns;
+
+ public synchronized void registerEventTypePattern(
+ final String patternString, final EventType eventType) {
+ eventTypePatterns.put(Pattern.compile(patternString), eventType);
+ }
+
+ /**
+ * Initializes the parsing engine with the standard log message patterns
+ * that should be included in current Tor. Any further patterns need to be
+ * added by the test application manually.
+ */
+ private void initializeEventTypePatterns() {
+ eventTypePatterns = new HashMap<Pattern, EventType>();
+ registerEventTypePattern("Opening Control listener on .*",
+ NodeEventType.NODE_CONTROL_PORT_OPENED);
+ registerEventTypePattern("Tor has successfully opened a circuit. "
+ + "Looks like client functionality is working.",
+ NodeEventType.NODE_CIRCUIT_OPENED);
+ registerEventTypePattern(
+ "Established circuit .* as introduction "
+ + "point for service .*",
+ HiddenServiceEventType.BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO);
+ registerEventTypePattern("Received INTRO_ESTABLISHED cell on "
+ + "circuit .* for service .*",
+ HiddenServiceEventType.BOB_INTRO_ESTABLISHED_RECEIVED);
+ registerEventTypePattern("Sending publish request for hidden "
+ + "service .*", HiddenServiceEventType.BOB_SENDING_PUBLISH_DESC);
+ registerEventTypePattern("Uploaded rendezvous descriptor",
+ HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED);
+ registerEventTypePattern("Received INTRODUCE2 cell for service .* "
+ + "on circ .*", HiddenServiceEventType.BOB_INTRODUCE2_RECEIVED);
+ registerEventTypePattern("Done building circuit .* to rendezvous "
+ + "with cookie .* for service .*",
+ HiddenServiceEventType.BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1);
+ registerEventTypePattern("begin is for rendezvous",
+ HiddenServiceEventType.BOB_APP_CONN_OPENED);
+ registerEventTypePattern("Got a hidden service request for ID '.*'",
+ HiddenServiceEventType.ALICE_ONION_REQUEST_RECEIVED);
+ registerEventTypePattern("Fetching rendezvous descriptor for "
+ + "service .*", HiddenServiceEventType.ALICE_SENDING_FETCH_DESC);
+ registerEventTypePattern("Received rendezvous descriptor",
+ HiddenServiceEventType.ALICE_DESC_FETCHED_RECEIVED);
+ registerEventTypePattern(
+ "Sending an ESTABLISH_RENDEZVOUS cell",
+ HiddenServiceEventType.ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS);
+ registerEventTypePattern("Got rendezvous ack. This circuit is now "
+ + "ready for rendezvous",
+ HiddenServiceEventType.ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED);
+ registerEventTypePattern("introcirc is open",
+ HiddenServiceEventType.ALICE_BUILT_INTRO_CIRC);
+ registerEventTypePattern("Sending an INTRODUCE1 cell",
+ HiddenServiceEventType.ALICE_SENDING_INTRODUCE1);
+ registerEventTypePattern("Received ack. Telling rend circ",
+ HiddenServiceEventType.ALICE_INTRODUCE_ACK_RECEIVED);
+ registerEventTypePattern(
+ "Got RENDEZVOUS2 cell from hidden service",
+ HiddenServiceEventType.ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED);
+ registerEventTypePattern("Handling rendezvous descriptor post",
+ HiddenServiceEventType.DIR_PUBLISH_DESC_RECEIVED);
+ registerEventTypePattern("Handling rendezvous descriptor get",
+ HiddenServiceEventType.DIR_FETCH_DESC_RECEIVED);
+ registerEventTypePattern(
+ "Received an ESTABLISH_INTRO request on circuit .*",
+ HiddenServiceEventType.IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED);
+ registerEventTypePattern(
+ "Received an INTRODUCE1 request on circuit .*",
+ HiddenServiceEventType.IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK);
+ registerEventTypePattern(
+ "Received an ESTABLISH_RENDEZVOUS request on circuit .*",
+ HiddenServiceEventType.RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED);
+ registerEventTypePattern(
+ "Got request for rendezvous from circuit .* to cookie .*",
+ HiddenServiceEventType.RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2);
+ }
+
+ /**
+ * Stores the given <code>event</code> from <code>source</code> to the
+ * event history and propagates its occurrence to all registered event
+ * handlers.
+ *
+ * @param event
+ * The observed event.
+ */
+ private synchronized void observeEvent(final Event event) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "observeEvent", event);
+
+ final String source = event.getSource();
+ logger.log(Level.FINE, "Observed event " + event + " from source "
+ + source + "!");
+
+ // remember observed event
+ if (!observedEvents.containsKey(event.getSource())) {
+ observedEvents.put(source, new ArrayList<Event>());
+ }
+ observedEvents.get(source).add(event);
+
+ // notify waiting threads
+ notifyAll();
+
+ // inform event listeners
+ if (eventHandlers.containsKey(source)) {
+
+ // make a copy of the event handler set, because some event handlers
+ // might want to remove themselves from this set while handling the
+ // event
+ final Set<EventListener> copyOfEventHandlers =
+ new HashSet<EventListener>(eventHandlers.get(source));
+
+ for (final EventListener eventHandler : copyOfEventHandlers) {
+
+ logger.log(Level.FINE, "Informing event listener "
+ + eventHandler + " about recently observed event "
+ + event + " from source " + source + "!");
+ eventHandler.handleEvent(event);
+
+ }
+ }
+
+ // make a copy of the event handler set for all sources, because some
+ // event handlers might want to remove themselves from this set while
+ // handling the event
+ final Set<EventListener> copyOfEventHandlersForAllSources =
+ new HashSet<EventListener>(eventHandlersForAllSources);
+
+ for (final EventListener eventHandler : copyOfEventHandlersForAllSources) {
+
+ logger.log(Level.FINE, "Informing event listener " + eventHandler
+ + " about recently observed event " + event
+ + " from source " + source + "!");
+ eventHandler.handleEvent(event);
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "observeEvent");
+ }
+
+ public synchronized void removeEventListener(
+ final EventListener eventListener) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "removeEventListener",
+ eventListener);
+
+ // check parameters
+ if (eventListener == null) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger
+ .throwing(this.getClass().getName(), "removeEventListener",
+ e);
+ throw e;
+ }
+
+ // don't know to which source this listener has been added (may to more
+ // than one), so remove it from all possible sets
+ for (final Set<EventListener> set : eventHandlers.values()) {
+ if (set.remove(eventListener)) {
+ logger.log(Level.FINE, "Removed event listener!");
+ }
+ }
+
+ // remove from event listeners for all sources
+ if (eventHandlersForAllSources.remove(eventListener)) {
+ logger.log(Level.FINE, "Removed event listener!");
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "removeEventListener");
+ }
+
+ public synchronized void waitForAnyOccurence(final String source,
+ final EventType event) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+ new Object[] { source, event });
+
+ // check parameters
+ if (source == null || event == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger
+ .throwing(this.getClass().getName(), "waitForAnyOccurence",
+ e);
+ throw e;
+ }
+
+ // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+ // means to wait forever
+ waitForAnyOccurence(source, event, -1L);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "waitForAnyOccurence");
+
+ }
+
+ public synchronized boolean waitForAnyOccurence(final String source,
+ final EventType event, final long maximumTimeToWaitInMillis) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+ new Object[] { source, event, maximumTimeToWaitInMillis });
+
+ // check parameters
+ if (source == null || event == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger
+ .throwing(this.getClass().getName(), "waitForAnyOccurence",
+ e);
+ throw e;
+ }
+
+ // check if we have already observed the event
+ if (hasEventOccured(source, event)) {
+ logger.log(Level.FINE, "Waiting for any occurence of event "
+ + event + " returned immediately!");
+ logger.exiting(this.getClass().getName(), "waitForAnyOccurence",
+ true);
+ return true;
+ }
+
+ // invoke method that waits for next occurence of the event
+ final boolean result =
+ waitForNextOccurence(source, event, maximumTimeToWaitInMillis);
+
+ // log exiting and return result
+ logger
+ .exiting(this.getClass().getName(), "waitForAnyOccurence",
+ result);
+ return result;
+ }
+
+ public synchronized void waitForNextOccurence(final String source,
+ final EventType event) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "waitForNextOccurence",
+ new Object[] { source, event });
+
+ // check parameters
+ if (source == null || event == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "waitForNextOccurence",
+ e);
+ throw e;
+ }
+
+ // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+ // means to wait forever
+ waitForNextOccurence(source, event, -1L);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "waitForNextOccurence");
+ }
+
+ public synchronized boolean waitForNextOccurence(final String source,
+ final EventType type, final long maximumTimeToWaitInMillis) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "waitForNextOccurence",
+ new Object[] { source, type, maximumTimeToWaitInMillis });
+
+ // check parameters
+ if (source == null || type == null || !eventSources.contains(source)) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "waitForNextOccurence",
+ e);
+ throw e;
+ }
+
+ // distinguish between negative waiting time (wait forever) and zero or
+ // positive waiting time
+ if (maximumTimeToWaitInMillis < 0) {
+
+ // wait forever
+ while (!hasEventOccured(source, type)) {
+
+ logger.log(Level.FINEST,
+ "We will wait infinetely for the next occurence of "
+ + "event type " + type + " from source "
+ + source + "...");
+ try {
+ wait();
+ } catch (final InterruptedException e) {
+ // don't handle
+ }
+
+ logger.log(Level.FINEST,
+ "We have been notified about an observed event while "
+ + "waiting for events of type " + type
+ + " from source " + source
+ + "; need to check whether the observed event "
+ + "is what we are looking for...");
+ }
+
+ logger.log(Level.FINE, "Waiting for occurence of event type "
+ + type + " succeeded!");
+
+ // log exiting and return result
+ logger.exiting(this.getClass().getName(), "waitForNextOccurence",
+ true);
+ return true;
+
+ }
+
+ // wait for the given time at most
+ final long endOfTime =
+ System.currentTimeMillis() + maximumTimeToWaitInMillis;
+ long timeLeft = 0;
+ while (!hasEventOccured(source, type)
+ && (timeLeft = endOfTime - System.currentTimeMillis()) > 0) {
+
+ logger.log(Level.FINEST, "We will wait for " + timeLeft
+ + " milliseconds for the next occurence of event type "
+ + type + " from source " + source + "...");
+
+ try {
+ wait(timeLeft);
+ } catch (final InterruptedException e) {
+ // don't handle
+ }
+
+ logger.log(Level.FINEST,
+ "We have been notified about an observed event while "
+ + "waiting for events of type " + type
+ + " from source " + source
+ + "; need to check whether the observed event "
+ + "is what we are looking for...");
+ }
+
+ // determine result
+ final boolean result = hasEventOccured(source, type);
+
+ logger.log(Level.FINE, "Waiting for next occurence of event type "
+ + type + " from source " + source
+ + (result ? " succeeded!" : " did not succeed!"));
+
+ // log exiting and return result
+ logger.exiting(this.getClass().getName(), "waitForNextOccurence",
+ result);
+ return result;
+ }
+
+ /**
+ * Adds the given <code>source</code> as possible event source.
+ *
+ * @param source
+ * The name of the node, client, or server to add.
+ * @throws IllegalArgumentException
+ * Thrown if there is already an event source with this name.
+ */
+ void addEventSource(final String source) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addEventSource", source);
+
+ // check if source name is unique in this network
+ if (eventSources.contains(source)) {
+ final IllegalArgumentException e =
+ new IllegalArgumentException(
+ "There is already an event source with name "
+ + source + " in this network!");
+ logger.throwing(this.getClass().getName(), "addEventSource", e);
+ throw e;
+ }
+
+ // add event source name
+ eventSources.add(source);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "addEventSource");
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
old mode 100755
new mode 100644
index a042f62..3503bd0
--- a/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
@@ -1,1108 +1,1108 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.uniba.wiai.lspi.puppetor.ClientApplication;
-import de.uniba.wiai.lspi.puppetor.DirectoryNode;
-import de.uniba.wiai.lspi.puppetor.Event;
-import de.uniba.wiai.lspi.puppetor.EventListener;
-import de.uniba.wiai.lspi.puppetor.EventManager;
-import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NodeEventType;
-import de.uniba.wiai.lspi.puppetor.NodeState;
-import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-import de.uniba.wiai.lspi.puppetor.RouterNode;
-import de.uniba.wiai.lspi.puppetor.ServerApplication;
-
-/**
- * Implementation of <code>Network</code>.
- *
- * @author kloesing
- */
-public class NetworkImpl implements Network {
-
- /**
- * Internal thread class that is used to start Tor processes in parallel.
- */
- private class NodeStarter extends Thread {
-
- /**
- * The exception, if one is caught while trying to start the node.
- */
- Exception caughtException;
-
- /**
- * The maximum time to wait for the Tor process to start in
- * milliseconds.
- */
- private final long maximumTimeToWaitInMillis;
-
- /**
- * The node for which the Tor process shall be started.
- */
- private final ProxyNode node;
-
- /**
- * Flag that denotes whether starting the Tor process was successful.
- */
- boolean success = false;
-
- /**
- * Creates a new <code>NodeStarter</code> for node <code>node</code>
- * that will wait for <code>maximumTimeToWaitInMillis</code>
- * milliseconds to start a Tor process, but that is not started, yet.
- *
- * @param node
- * The node for which the Tor process shall be started.
- * @param maximumTimeToWaitInMillis
- * The maximum time to wait for the Tor process to start in
- * milliseconds.
- */
- NodeStarter(final ProxyNode node, final long maximumTimeToWaitInMillis) {
-
- // log entering
- logger.entering(this.getClass().getName(), "NodeStarter",
- new Object[] { node, maximumTimeToWaitInMillis });
-
- // store parameters
- this.node = node;
- this.maximumTimeToWaitInMillis = maximumTimeToWaitInMillis;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "NodeStarter");
- }
-
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- try {
- // try to start node
- success = node.startNode(maximumTimeToWaitInMillis);
- } catch (final PuppeTorException e) {
- logger.log(Level.SEVERE,
- "Caught an exception while starting node "
- + node.toString() + "!");
- caughtException = e;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- }
-
- /**
- * Event manager to which all events concerning this network are notified.
- */
- private final EventManagerImpl eventManager;
-
- /**
- * Logger for this network which is called "network." plus the name of this
- * network.
- */
- private final Logger logger;
-
- /**
- * Contains the name of this network configuration which is the String
- * conversion of System.currentTimeMillis() of the network creation time.
- */
- private final String networkName;
-
- /**
- * All nodes contained in this network.
- */
- private final Map<String, ProxyNode> nodes;
-
- /**
- * Directory that contains status information of all nodes contained in this
- * network.
- */
- private final File workingDir;
-
- /**
- * The counter for automatically assigned port numbers created by this
- * <code>Network</code>.
- */
- private int portCounter = 7000;
-
- /**
- * Creates an initially unpopulated Tor network and creates a new working
- * directory for it at test-env/randomTestID/.
- *
- * @param networkName
- * Name of this network configuration. May neither be
- * <code>null</code> or a zero-length string.
- * @param startPort
- * The initial value for automatically assigned port numbers of
- * nodes created by this <code>Network</code>; must be a value
- * between 1024 and 65535.
- * @throws IllegalArgumentException
- * Thrown if the given <code>networkName</code> is either
- * <code>null</code> or a zero-length string, or if an illegal
- * number is given for <code>startPort</code>.
- */
- public NetworkImpl(final String networkName, final int startPort) {
- // initialize using overloaded constructor
- this(networkName);
-
- // check if start port is valid
- if (startPort < 1024 || startPort > 65535) {
- throw new IllegalArgumentException("Invalid startPort: "
- + startPort);
- }
-
- // remember parameter
- portCounter = startPort;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "NetworkImpl");
- }
-
- /**
- * Creates an initially unpopulated Tor network and creates a new working
- * directory for it at test-env/randomTestID/.
- *
- * @param networkName
- * Name of this network configuration. May neither be
- * <code>null</code> or a zero-length string.
- * @throws IllegalArgumentException
- * Thrown if the given <code>networkName</code> is either
- * <code>null</code> or a zero-length string.
- */
- public NetworkImpl(final String networkName) {
-
- // check if networkName can be used as logger name
- if (networkName == null || networkName.length() == 0) {
- throw new IllegalArgumentException("Invalid networkName: "
- + networkName);
- }
-
- // create logger
- logger = Logger.getLogger("network." + networkName);
-
- // log entering
- logger.entering(this.getClass().getName(), "NetworkImpl", networkName);
-
- // TODO is this necessary?!
- logger.setLevel(Level.ALL);
-
- // remember parameter
- this.networkName = networkName;
-
- // create working directory
- workingDir = new File("test-env/" + System.currentTimeMillis());
- workingDir.mkdirs();
- logger.log(Level.FINE, "Created working directory \""
- + workingDir.getAbsolutePath() + "\"");
-
- // TODO if we want to log to file, set this... somehow...
- // this.logFile = new File(this.workingDir.getAbsolutePath()
- // + "\\events.log");
-
- // initialize data structures
- nodes = new ConcurrentHashMap<String, ProxyNode>();
-
- // create event manager
- eventManager = new EventManagerImpl(this.networkName);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "NetworkImpl");
- }
-
- /**
- * Returns whether all nodes in this network are up, or not.
- *
- * @return <code>true</code> if all nodes are up, <code>false</code> if
- * at least one node is not up.
- */
- private boolean allNodesUp() {
-
- // log entering
- logger.entering(this.getClass().getName(), "allNodesUp");
-
- // fail on first node that is not up
- for (final ProxyNode node : nodes.values()) {
- if (!eventManager.hasEventOccured(node.getNodeName(),
- NodeEventType.NODE_CIRCUIT_OPENED)) {
-
- // log exiting and return false
- logger.exiting(this.getClass().getName(), "allNodesUp");
- return false;
- }
- }
-
- // log exiting and return true
- logger.exiting(this.getClass().getName(), "allNodesUp");
- return true;
- }
-
- public void configureAsPrivateNetwork() throws PuppeTorException {
- // log entering
- logger.entering(this.getClass().getName(), "configureAsPrivateNetwork");
-
- // read DirServer strings for all directories
- final List<String> authorizedDirectoriesFingerprints =
- new ArrayList<String>();
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof DirectoryNode) {
- final DirectoryNode dirNode = (DirectoryNode) node;
- authorizedDirectoriesFingerprints.add(dirNode
- .getDirServerString());
- }
- }
-
- this.configureAsPartOfPrivateNetwork(authorizedDirectoriesFingerprints);
-
- // read fingerprints for all directories and routers
- final HashSet<String> approvedRoutersFingerprints =
- new HashSet<String>();
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof RouterNode) {
- final RouterNode routerOrDirNode = (RouterNode) node;
- approvedRoutersFingerprints.add(routerOrDirNode
- .getFingerprint());
- }
- }
-
- // write fingerprints for all directories and routers to the
- // approved-routers file
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof DirectoryNode) {
- final DirectoryNode dirNode = (DirectoryNode) node;
- dirNode.addApprovedRouters(approvedRoutersFingerprints);
- }
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "configureAsPrivateNetwork");
- }
-
- public void configureAsPartOfPrivateNetwork(
- List<String> authorizedDirectoriesFingerprints) {
- //throws PuppeTorException {
- // log entering
- logger.entering(this.getClass().getName(), "configureAsPartOfPrivateNetwork");
-
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() == NodeState.CONFIGURING ||
- node.getNodeState() == NodeState.CONFIGURATION_WRITTEN) {
- // add to configuration
- node.addConfiguration("TestingTorNetwork 1");
- }
- }
-
- // configure nodes
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() == NodeState.CONFIGURING ||
- node.getNodeState() == NodeState.CONFIGURATION_WRITTEN) {
- // add to configuration
- node.addConfigurations(authorizedDirectoriesFingerprints);
- }
- }
-
-/* // read fingerprints for all directories and routers
- final HashSet<String> approvedRoutersFingerprints =
- new HashSet<String>();
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof RouterNode) {
- final RouterNode routerOrDirNode = (RouterNode) node;
- approvedRoutersFingerprints.add(routerOrDirNode
- .getFingerprint());
- }
- }
-
- // write fingerprints for all directories and routers to the
- // approved-routers file
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof DirectoryNode) {
- final DirectoryNode dirNode = (DirectoryNode) node;
- dirNode.addApprovedRouters(approvedRoutersFingerprints);
- }
- }
-*/
- // log exiting
- logger.exiting(this.getClass().getName(), "configureAsPartOfPrivateNetwork");
- }
-
- public ClientApplication createClient(final String clientApplicationName,
- final String targetAddress, final int targetPort,
- final int socksPort) {
- // log entering
- logger.entering(this.getClass().getName(), "createClient",
- new Object[] { clientApplicationName, targetAddress,
- targetPort, socksPort });
-
- // create client; parameter checking is done in constructor
- final ClientApplicationImpl client =
- new ClientApplicationImpl(this, clientApplicationName,
- targetAddress, targetPort, socksPort);
-
- // add name to event manager as event source
- eventManager.addEventSource(clientApplicationName);
-
- // log exiting and return client
- logger.exiting(this.getClass().getName(), "createClient", client);
- return client;
- }
-
- public DirectoryNode createDirectory(final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort, final String serverIpAddress) {
- // log entering
- logger.entering(this.getClass().getName(), "createDirectory",
- new Object[] { nodeName, controlPort, socksPort, orPort,
- dirPort, serverIpAddress });
-
- // create directory node; parameter checking is done in constructor
- final DirectoryNode dir =
- new DirectoryNodeImpl(this, nodeName, controlPort, socksPort,
- orPort, dirPort, serverIpAddress);
-
- // add new directory node to nodes collection
- nodes.put(nodeName, dir);
-
- // add name to event manager as event source
- eventManager.addEventSource(nodeName);
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createDirectory", dir);
- return dir;
- }
-
- public DirectoryNode createDirectory(final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createDirectory",
- new Object[] { nodeName, controlPort, socksPort, orPort,
- dirPort });
-
- // invoke overloaded method
- final DirectoryNode dir =
- this.createDirectory(nodeName, controlPort, socksPort, orPort,
- dirPort, "127.0.0.1");
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createDirectory", dir);
- return dir;
- }
-
- public DirectoryNode createDirectory(final String nodeName,
- final String serverIpAddress) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createDirectory",
- new Object[] { nodeName, serverIpAddress });
-
- // invoke overloaded method
- final DirectoryNode dir =
- this.createDirectory(nodeName, portCounter++, portCounter++,
- portCounter++, portCounter++, serverIpAddress);
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createDirectory", dir);
- return dir;
- }
-
- public DirectoryNode createDirectory(final String nodeName) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createDirectory", nodeName);
-
- // invoke overloaded method
- final DirectoryNode dir =
- this.createDirectory(nodeName, portCounter++, portCounter++,
- portCounter++, portCounter++, "127.0.0.1");
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createDirectory", dir);
- return dir;
- }
-
- public ProxyNode createProxy(final String nodeName, final int controlPort,
- final int socksPort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createProxy", new Object[] {
- nodeName, controlPort, socksPort });
-
- // create proxy node; parameter checking is done in constructor
- final ProxyNode proxy =
- new ProxyNodeImpl(this, nodeName, controlPort, socksPort);
-
- // add new proxy node to nodes collection
- nodes.put(nodeName, proxy);
-
- // add name to event manager as event source
- eventManager.addEventSource(nodeName);
-
- // log exiting and return proxy node
- logger.exiting(this.getClass().getName(), "createProxy", proxy);
- return proxy;
- }
-
- public ProxyNode createProxy(final String nodeName) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createProxy", nodeName);
-
- // invoke overloaded method
- final ProxyNode proxy =
- this.createProxy(nodeName, portCounter++, portCounter++);
-
- // log exiting and return proxy node
- logger.exiting(this.getClass().getName(), "createProxy", proxy);
- return proxy;
- }
-
- public RouterNode createRouter(final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort, final String serverIpAddress) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createRouter",
- new Object[] { nodeName, controlPort, socksPort, orPort,
- dirPort, serverIpAddress });
-
- // create router node; parameter checking is done in constructor
- final RouterNode router =
- new RouterNodeImpl(this, nodeName, controlPort, socksPort,
- orPort, dirPort, serverIpAddress);
-
- // add new router node to nodes collection
- nodes.put(nodeName, router);
-
- // add name to event manager as event source
- eventManager.addEventSource(nodeName);
-
- // log exiting and return router node
- logger.exiting(this.getClass().getName(), "createRouter", router);
- return router;
- }
-
- public RouterNode createRouter(final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createRouter",
- new Object[] { nodeName, controlPort, socksPort, orPort,
- dirPort });
-
- // invoke overloaded method
- final DirectoryNode dir =
- this.createDirectory(nodeName, controlPort, socksPort, orPort,
- dirPort, "127.0.0.1");
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createRouter", dir);
- return dir;
- }
-
- public RouterNode createRouter(final String nodeName,
- final String serverIpAddress) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createRouter",
- new Object[] { nodeName, serverIpAddress });
-
- // invoke overloaded method
- final RouterNode dir =
- this.createRouter(nodeName, portCounter++, portCounter++,
- portCounter++, portCounter++, serverIpAddress);
-
- // log exiting and return directory node
- logger.exiting(this.getClass().getName(), "createRouter", dir);
- return dir;
- }
-
- public RouterNode createRouter(final String nodeName) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createRouter", nodeName);
-
- // invoke overloaded method
- final RouterNode router =
- this.createRouter(nodeName, portCounter++, portCounter++,
- portCounter++, portCounter++, "127.0.0.1");
-
- // log exiting and return router node
- logger.exiting(this.getClass().getName(), "createRouter", router);
- return router;
- }
-
- public ServerApplication createServer(final String serverApplicationName,
- final int serverPort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createServer",
- new Object[] { serverApplicationName, serverPort });
-
- // create server; parameter checking is done in constructor
- final ServerApplicationImpl server =
- new ServerApplicationImpl(this, serverApplicationName,
- serverPort);
-
- // add name to event manager as event source
- eventManager.addEventSource(serverApplicationName);
-
- // log exiting and return server
- logger.exiting(this.getClass().getName(), "createServer", server);
- return server;
- }
-
- public ServerApplication createServer(final String serverApplicationName) {
-
- // log entering
- logger.entering(this.getClass().getName(), "createServer",
- serverApplicationName);
-
- // invoke overloaded method
- final ServerApplication server =
- this.createServer(serverApplicationName, portCounter++);
-
- // log exiting and return server
- logger.exiting(this.getClass().getName(), "createServer", server);
- return server;
- }
-
- public EventManager getEventManager() {
- return eventManager;
- }
-
- /**
- * Returns the implementation instance of the event manager of this network.
- *
- * @return The implementation instance of the event manager of this network.
- */
- public EventManagerImpl getEventManagerImpl() {
- return eventManager;
- }
-
- public File getWorkingDirectory() {
- return workingDir;
- }
-
- public ProxyNode getNode(final String nodeName) {
- return nodes.get(nodeName);
- }
-
- public Map<String, ProxyNode> getAllProxyNodes() {
- final Map<String, ProxyNode> result = new HashMap<String, ProxyNode>();
- for (final String nodeName : nodes.keySet()) {
- final ProxyNode node = nodes.get(nodeName);
- if (!(node instanceof RouterNode)) {
- result.put(nodeName, node);
- }
- }
- return result;
- }
-
- public Map<String, RouterNode> getAllRouterNodes() {
- final Map<String, RouterNode> result =
- new HashMap<String, RouterNode>();
- for (final String nodeName : nodes.keySet()) {
- final ProxyNode node = nodes.get(nodeName);
- if (node instanceof RouterNode && !(node instanceof DirectoryNode)) {
- result.put(nodeName, (RouterNode) node);
- }
- }
- return result;
- }
-
- public Map<String, DirectoryNode> getAllDirectoryNodes() {
- final Map<String, DirectoryNode> result =
- new HashMap<String, DirectoryNode>();
- for (final String nodeName : nodes.keySet()) {
- final ProxyNode node = nodes.get(nodeName);
- if (node instanceof DirectoryNode) {
- result.put(nodeName, (DirectoryNode) node);
- }
- }
- return result;
- }
-
- public Map<String, ProxyNode> getAllNodes() {
- return new HashMap<String, ProxyNode>(nodes);
- }
-
- public boolean hupUntilUp(final int tries, final long hupInterval)
- throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "hupUntilUp", new Object[] {
- tries, hupInterval });
-
- // check if all nodes are running
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() != NodeState.RUNNING) {
- final IllegalStateException e =
- new IllegalStateException(
- "All nodes must be running before sending them HUP "
- + "commands!");
- logger.throwing(this.getClass().getName(), "hupUntilUp", e);
- throw e;
- }
- }
-
- // check if nodes are already up; if so, return immediately
- if (allNodesUp()) {
-
- // log exiting and return true
- logger.exiting(this.getClass().getName(), "hupUntilUp", true);
- return true;
- }
-
- final Object hupLock = new Object();
- // create and register a new event handler for each node
- for (final ProxyNode node : nodes.values()) {
- eventManager.addEventListener(node.getNodeName(),
- new EventListener() {
- public void handleEvent(final Event event) {
- if (event.getType() == NodeEventType.NODE_CIRCUIT_OPENED) {
- synchronized(hupLock) {
- hupLock.notify();
- }
- eventManager.removeEventListener(this);
- }
- }
- });
- }
-
- // walk through wait-check-hup loop until there are no tries left
- for (int i = 0; i < tries; i++) {
-
- // determine how long to try waiting for the hangup
- final long endOfSleeping = System.currentTimeMillis() + hupInterval;
-
- // unless all nodes have reported to be up, wait for the given
- // maximum time
- while (System.currentTimeMillis() < endOfSleeping) {
-
- synchronized(hupLock) {
- // check if nodes are up now
- if (allNodesUp()) {
-
- // log exiting and return true
- logger.exiting(this.getClass().getName(), "hupUntilUp",
- true);
- return true;
- }
- // sleep
- try {
- hupLock.wait(hupInterval);
- } catch (final InterruptedException e) {
- // do nothing about it
- }
- }
- }
-
- logger.log(Level.FINE, "Sending HUP to nodes");
- // send a HUP signal to all nodes
- for (final ProxyNode node : nodes.values()) {
- logger
- .log(Level.FINE, "Sending HUP to node "
- + node.toString());
- node.hup();
- }
-
- // continue in loop
- }
-
- // no retries left and not all nodes are up; log exiting and return
- // failure
- logger.exiting(this.getClass().getName(), "hupUntilUp", false);
- return false;
- }
-
- public void hupAllNodes() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "hupAllNodes");
-
- // check if all nodes are running
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() != NodeState.RUNNING) {
- final IllegalStateException e =
- new IllegalStateException(
- "All nodes must be running before sending them HUP "
- + "commands!");
- logger.throwing(this.getClass().getName(), "hupAllNodes", e);
- throw e;
- }
- }
-
- // send a HUP signal to all nodes
- for (final ProxyNode node : nodes.values()) {
- logger.log(Level.FINE, "Sending HUP to node " + node.toString());
- node.hup();
- }
-
- // no retries left and not all nodes are up; log exiting and return
- // failure
- logger.exiting(this.getClass().getName(), "hupAllNodes");
- }
-
- public void hupAllDirectories() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "hupAllDirectories");
-
- // check if all directory nodes are running
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof DirectoryNode
- && node.getNodeState() != NodeState.RUNNING) {
- final IllegalStateException e =
- new IllegalStateException(
- "All directory nodes must be running before sending "
- + "them HUP commands!");
- logger.throwing(this.getClass().getName(), "hupAllDirectories",
- e);
- throw e;
- }
- }
-
- // send a HUP signal to all nodes
- for (final ProxyNode node : nodes.values()) {
- if (node instanceof DirectoryNode) {
- logger
- .log(Level.FINE, "Sending HUP to node "
- + node.toString());
- node.hup();
- }
- }
-
- // no retries left and not all nodes are up; log exiting and return
- // failure
- logger.exiting(this.getClass().getName(), "hupAllDirectories");
- }
-
- public void shutdownNodes() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "shutdownNodes");
-
- // iteratively shut down all running nodes; if an exception is caught,
- // continue shutting down the other nodes and throw the first exception
- // subsequently
- PuppeTorException firstCaughtException = null;
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() == NodeState.RUNNING) {
- try {
- node.shutdown();
- } catch (final PuppeTorException e) {
- if (firstCaughtException == null) {
- firstCaughtException = e;
- }
- }
- }
- }
-
- // if an exception was caught during shutting down nodes, throw the
- // first caught exception
- if (firstCaughtException != null) {
- logger.throwing(this.getClass().getName(), "shutdownNodes",
- firstCaughtException);
- throw firstCaughtException;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "shutdownNodes");
- }
-
- public boolean startNodes(final long maximumTimeToWaitInMillis)
- throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "startNodes",
- maximumTimeToWaitInMillis);
-
- // check node states
- for (final ProxyNode node : nodes.values()) {
- if (node.getNodeState() != NodeState.CONFIGURATION_WRITTEN) {
- final IllegalStateException e =
- new IllegalStateException(
- "All configurations must be written before starting "
- + "nodes!");
- logger.throwing(this.getClass().getName(), "startNodes", e);
- throw e;
- }
- }
-
- // check parameter
- if (maximumTimeToWaitInMillis < 0) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "startNodes", e);
- throw e;
- }
-
- // remember time when we begin starting the nodes
- final long before = System.currentTimeMillis();
-
- // start nodes in parallel
- final Set<NodeStarter> allNodeStarters = new HashSet<NodeStarter>();
- for (final ProxyNode node : nodes.values()) {
- final NodeStarter nodeStarter =
- new NodeStarter(node, maximumTimeToWaitInMillis);
- allNodeStarters.add(nodeStarter);
- nodeStarter.start();
- }
-
- // wait for all node starts to complete
- for (final NodeStarter nodeStarter : allNodeStarters) {
-
- // join node starts one after the other
- try {
- nodeStarter.join();
- } catch (final InterruptedException e) {
- // this happens?! we have some kind of problem here!
- logger.log(Level.WARNING,
- "Interrupt while joining node starter!");
-
- // log exiting and return false
- logger.exiting(this.getClass().getName(), "startNodes", false);
- return false;
- }
-
- // if any thread has caught an exception, throw that exception now
- final Exception caughtException = nodeStarter.caughtException;
- if (caughtException != null) {
- final PuppeTorException ex =
- new PuppeTorException("Exception while starting node "
- + nodeStarter.node.getNodeName(),
- caughtException);
- logger.throwing(this.getClass().getName(), "startNodes", ex);
- throw ex;
- }
-
- // if node start did not succeed in the given time, fail
- if (!nodeStarter.success) {
- logger.log(Level.WARNING,
- "Starting nodes was not successful in "
- + maximumTimeToWaitInMillis / 1000
- + " seconds.", networkName);
-
- // log exiting and return false
- logger.exiting(this.getClass().getName(), "startNodes", false);
- return false;
- }
- }
-
- // determine how long we took to start all nodes
- final long after = System.currentTimeMillis();
- logger.log(Level.FINE, "Starting nodes was successful and took "
- + (after - before) / 1000 + " seconds.", networkName);
-
- // log exiting and return true
- logger.exiting(this.getClass().getName(), "startNodes", true);
- return true;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": networkName=\""
- + networkName;
- }
-
- public String getNetworkName() {
- return networkName;
- }
-
- public void writeConfigurations() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "writeConfigurations");
-
- // write configurations for all nodes
- for (final ProxyNode node : nodes.values()) {
- node.writeConfiguration();
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "writeConfigurations");
- }
-
- public void addTemplateConfiguration(
- final Class<? extends ProxyNode> nodeClass,
- final String templateConfigurationString) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addTemplateConfiguration",
- new Object[] { nodeClass, templateConfigurationString });
-
- // check parameters
- if (nodeClass == null || templateConfigurationString == null
- || templateConfigurationString.length() < 1
- || !templateConfigurationString.contains(" ")) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "addTemplateConfiguration", e);
- throw e;
- }
-
- // add template string to appropriate template configuration
- if (nodeClass == ProxyNode.class) {
- ProxyNodeImpl.templateConfiguration
- .add(templateConfigurationString);
- } else if (nodeClass == RouterNode.class) {
- RouterNodeImpl.templateConfiguration
- .add(templateConfigurationString);
- } else if (nodeClass == DirectoryNode.class) {
- DirectoryNodeImpl.templateConfiguration
- .add(templateConfigurationString);
- } else {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "addTemplateConfiguration", e);
- throw e;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "addTemplateConfiguration");
- }
-
- public List<String> getTemplateConfiguration(
- final Class<? extends ProxyNode> nodeClass) {
-
- // log entering
- logger.entering(this.getClass().getName(), "getTemplateConfiguration",
- nodeClass);
-
- // check parameter
- if (nodeClass == null) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "getTemplateConfiguration", e);
- throw e;
- }
-
- // obtain reference on appropriate template configuration
- List<String> result = null;
- if (nodeClass == ProxyNode.class) {
- result = new ArrayList<String>(ProxyNodeImpl.templateConfiguration);
- } else if (nodeClass == RouterNode.class) {
- result =
- new ArrayList<String>(RouterNodeImpl.templateConfiguration);
- } else if (nodeClass == DirectoryNode.class) {
- result =
- new ArrayList<String>(
- DirectoryNodeImpl.templateConfiguration);
- } else {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "getTemplateConfiguration", e);
- throw e;
- }
-
- // log exiting and return result
- logger.exiting(this.getClass().getName(), "getTemplateConfiguration",
- result);
- return result;
- }
-
- public void removeTemplateConfiguration(
- final Class<? extends ProxyNode> nodeClass,
- final String templateConfigurationKey) {
-
- // log entering
- logger.entering(this.getClass().getName(),
- "removeTemplateConfiguration", new Object[] { nodeClass,
- templateConfigurationKey });
-
- // check parameters
- if (nodeClass == null || templateConfigurationKey == null
- || templateConfigurationKey.length() < 1) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "removeTemplateConfiguration", e);
- throw e;
- }
-
- // obtain reference on appropriate template configuration
- List<String> templateConfig = null;
- if (nodeClass == ProxyNode.class) {
- templateConfig = ProxyNodeImpl.templateConfiguration;
- } else if (nodeClass == RouterNode.class) {
- templateConfig = RouterNodeImpl.templateConfiguration;
- } else if (nodeClass == DirectoryNode.class) {
- templateConfig = DirectoryNodeImpl.templateConfiguration;
- } else {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(),
- "removeTemplateConfiguration", e);
- throw e;
- }
-
- // iterate over existing template configuration strings and remove all
- // configuration strings that have the given configuration key
- final List<String> configurationStringsToRemove =
- new ArrayList<String>();
- for (final String currentConfigurationString : templateConfig) {
- final String currentConfigurationKey =
- currentConfigurationString.substring(0,
- currentConfigurationString.indexOf(" "));
- if (currentConfigurationKey.equals(templateConfigurationKey)) {
- configurationStringsToRemove.add(currentConfigurationString);
- }
- }
- templateConfig.removeAll(configurationStringsToRemove);
-
- // log exiting
- logger
- .exiting(this.getClass().getName(),
- "removeTemplateConfiguration");
- }
-
- /**
- * Returns the current port number and increments it afterwards.
- *
- * @return The current port number.
- */
- int getNextPortNumber() {
- return portCounter++;
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NodeEventType;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+
+/**
+ * Implementation of <code>Network</code>.
+ *
+ * @author kloesing
+ */
+public class NetworkImpl implements Network {
+
+ /**
+ * Internal thread class that is used to start Tor processes in parallel.
+ */
+ private class NodeStarter extends Thread {
+
+ /**
+ * The exception, if one is caught while trying to start the node.
+ */
+ Exception caughtException;
+
+ /**
+ * The maximum time to wait for the Tor process to start in
+ * milliseconds.
+ */
+ private final long maximumTimeToWaitInMillis;
+
+ /**
+ * The node for which the Tor process shall be started.
+ */
+ private final ProxyNode node;
+
+ /**
+ * Flag that denotes whether starting the Tor process was successful.
+ */
+ boolean success = false;
+
+ /**
+ * Creates a new <code>NodeStarter</code> for node <code>node</code>
+ * that will wait for <code>maximumTimeToWaitInMillis</code>
+ * milliseconds to start a Tor process, but that is not started, yet.
+ *
+ * @param node
+ * The node for which the Tor process shall be started.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait for the Tor process to start in
+ * milliseconds.
+ */
+ NodeStarter(final ProxyNode node, final long maximumTimeToWaitInMillis) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "NodeStarter",
+ new Object[] { node, maximumTimeToWaitInMillis });
+
+ // store parameters
+ this.node = node;
+ this.maximumTimeToWaitInMillis = maximumTimeToWaitInMillis;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "NodeStarter");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+ // try to start node
+ success = node.startNode(maximumTimeToWaitInMillis);
+ } catch (final PuppeTorException e) {
+ logger.log(Level.SEVERE,
+ "Caught an exception while starting node "
+ + node.toString() + "!");
+ caughtException = e;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * Event manager to which all events concerning this network are notified.
+ */
+ private final EventManagerImpl eventManager;
+
+ /**
+ * Logger for this network which is called "network." plus the name of this
+ * network.
+ */
+ private final Logger logger;
+
+ /**
+ * Contains the name of this network configuration which is the String
+ * conversion of System.currentTimeMillis() of the network creation time.
+ */
+ private final String networkName;
+
+ /**
+ * All nodes contained in this network.
+ */
+ private final Map<String, ProxyNode> nodes;
+
+ /**
+ * Directory that contains status information of all nodes contained in this
+ * network.
+ */
+ private final File workingDir;
+
+ /**
+ * The counter for automatically assigned port numbers created by this
+ * <code>Network</code>.
+ */
+ private int portCounter = 7000;
+
+ /**
+ * Creates an initially unpopulated Tor network and creates a new working
+ * directory for it at test-env/randomTestID/.
+ *
+ * @param networkName
+ * Name of this network configuration. May neither be
+ * <code>null</code> or a zero-length string.
+ * @param startPort
+ * The initial value for automatically assigned port numbers of
+ * nodes created by this <code>Network</code>; must be a value
+ * between 1024 and 65535.
+ * @throws IllegalArgumentException
+ * Thrown if the given <code>networkName</code> is either
+ * <code>null</code> or a zero-length string, or if an illegal
+ * number is given for <code>startPort</code>.
+ */
+ public NetworkImpl(final String networkName, final int startPort) {
+ // initialize using overloaded constructor
+ this(networkName);
+
+ // check if start port is valid
+ if (startPort < 1024 || startPort > 65535) {
+ throw new IllegalArgumentException("Invalid startPort: "
+ + startPort);
+ }
+
+ // remember parameter
+ portCounter = startPort;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "NetworkImpl");
+ }
+
+ /**
+ * Creates an initially unpopulated Tor network and creates a new working
+ * directory for it at test-env/randomTestID/.
+ *
+ * @param networkName
+ * Name of this network configuration. May neither be
+ * <code>null</code> or a zero-length string.
+ * @throws IllegalArgumentException
+ * Thrown if the given <code>networkName</code> is either
+ * <code>null</code> or a zero-length string.
+ */
+ public NetworkImpl(final String networkName) {
+
+ // check if networkName can be used as logger name
+ if (networkName == null || networkName.length() == 0) {
+ throw new IllegalArgumentException("Invalid networkName: "
+ + networkName);
+ }
+
+ // create logger
+ logger = Logger.getLogger("network." + networkName);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "NetworkImpl", networkName);
+
+ // TODO is this necessary?!
+ logger.setLevel(Level.ALL);
+
+ // remember parameter
+ this.networkName = networkName;
+
+ // create working directory
+ workingDir = new File("test-env/" + System.currentTimeMillis());
+ workingDir.mkdirs();
+ logger.log(Level.FINE, "Created working directory \""
+ + workingDir.getAbsolutePath() + "\"");
+
+ // TODO if we want to log to file, set this... somehow...
+ // this.logFile = new File(this.workingDir.getAbsolutePath()
+ // + "\\events.log");
+
+ // initialize data structures
+ nodes = new ConcurrentHashMap<String, ProxyNode>();
+
+ // create event manager
+ eventManager = new EventManagerImpl(this.networkName);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "NetworkImpl");
+ }
+
+ /**
+ * Returns whether all nodes in this network are up, or not.
+ *
+ * @return <code>true</code> if all nodes are up, <code>false</code> if
+ * at least one node is not up.
+ */
+ private boolean allNodesUp() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "allNodesUp");
+
+ // fail on first node that is not up
+ for (final ProxyNode node : nodes.values()) {
+ if (!eventManager.hasEventOccured(node.getNodeName(),
+ NodeEventType.NODE_CIRCUIT_OPENED)) {
+
+ // log exiting and return false
+ logger.exiting(this.getClass().getName(), "allNodesUp");
+ return false;
+ }
+ }
+
+ // log exiting and return true
+ logger.exiting(this.getClass().getName(), "allNodesUp");
+ return true;
+ }
+
+ public void configureAsPrivateNetwork() throws PuppeTorException {
+ // log entering
+ logger.entering(this.getClass().getName(), "configureAsPrivateNetwork");
+
+ // read DirServer strings for all directories
+ final List<String> authorizedDirectoriesFingerprints =
+ new ArrayList<String>();
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof DirectoryNode) {
+ final DirectoryNode dirNode = (DirectoryNode) node;
+ authorizedDirectoriesFingerprints.add(dirNode
+ .getDirServerString());
+ }
+ }
+
+ this.configureAsPartOfPrivateNetwork(authorizedDirectoriesFingerprints);
+
+ // read fingerprints for all directories and routers
+ final HashSet<String> approvedRoutersFingerprints =
+ new HashSet<String>();
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof RouterNode) {
+ final RouterNode routerOrDirNode = (RouterNode) node;
+ approvedRoutersFingerprints.add(routerOrDirNode
+ .getFingerprint());
+ }
+ }
+
+ // write fingerprints for all directories and routers to the
+ // approved-routers file
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof DirectoryNode) {
+ final DirectoryNode dirNode = (DirectoryNode) node;
+ dirNode.addApprovedRouters(approvedRoutersFingerprints);
+ }
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "configureAsPrivateNetwork");
+ }
+
+ public void configureAsPartOfPrivateNetwork(
+ List<String> authorizedDirectoriesFingerprints) {
+ //throws PuppeTorException {
+ // log entering
+ logger.entering(this.getClass().getName(), "configureAsPartOfPrivateNetwork");
+
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() == NodeState.CONFIGURING ||
+ node.getNodeState() == NodeState.CONFIGURATION_WRITTEN) {
+ // add to configuration
+ node.addConfiguration("TestingTorNetwork 1");
+ }
+ }
+
+ // configure nodes
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() == NodeState.CONFIGURING ||
+ node.getNodeState() == NodeState.CONFIGURATION_WRITTEN) {
+ // add to configuration
+ node.addConfigurations(authorizedDirectoriesFingerprints);
+ }
+ }
+
+/* // read fingerprints for all directories and routers
+ final HashSet<String> approvedRoutersFingerprints =
+ new HashSet<String>();
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof RouterNode) {
+ final RouterNode routerOrDirNode = (RouterNode) node;
+ approvedRoutersFingerprints.add(routerOrDirNode
+ .getFingerprint());
+ }
+ }
+
+ // write fingerprints for all directories and routers to the
+ // approved-routers file
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof DirectoryNode) {
+ final DirectoryNode dirNode = (DirectoryNode) node;
+ dirNode.addApprovedRouters(approvedRoutersFingerprints);
+ }
+ }
+*/
+ // log exiting
+ logger.exiting(this.getClass().getName(), "configureAsPartOfPrivateNetwork");
+ }
+
+ public ClientApplication createClient(final String clientApplicationName,
+ final String targetAddress, final int targetPort,
+ final int socksPort) {
+ // log entering
+ logger.entering(this.getClass().getName(), "createClient",
+ new Object[] { clientApplicationName, targetAddress,
+ targetPort, socksPort });
+
+ // create client; parameter checking is done in constructor
+ final ClientApplicationImpl client =
+ new ClientApplicationImpl(this, clientApplicationName,
+ targetAddress, targetPort, socksPort);
+
+ // add name to event manager as event source
+ eventManager.addEventSource(clientApplicationName);
+
+ // log exiting and return client
+ logger.exiting(this.getClass().getName(), "createClient", client);
+ return client;
+ }
+
+ public DirectoryNode createDirectory(final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort, final String serverIpAddress) {
+ // log entering
+ logger.entering(this.getClass().getName(), "createDirectory",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort, serverIpAddress });
+
+ // create directory node; parameter checking is done in constructor
+ final DirectoryNode dir =
+ new DirectoryNodeImpl(this, nodeName, controlPort, socksPort,
+ orPort, dirPort, serverIpAddress);
+
+ // add new directory node to nodes collection
+ nodes.put(nodeName, dir);
+
+ // add name to event manager as event source
+ eventManager.addEventSource(nodeName);
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createDirectory", dir);
+ return dir;
+ }
+
+ public DirectoryNode createDirectory(final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createDirectory",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort });
+
+ // invoke overloaded method
+ final DirectoryNode dir =
+ this.createDirectory(nodeName, controlPort, socksPort, orPort,
+ dirPort, "127.0.0.1");
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createDirectory", dir);
+ return dir;
+ }
+
+ public DirectoryNode createDirectory(final String nodeName,
+ final String serverIpAddress) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createDirectory",
+ new Object[] { nodeName, serverIpAddress });
+
+ // invoke overloaded method
+ final DirectoryNode dir =
+ this.createDirectory(nodeName, portCounter++, portCounter++,
+ portCounter++, portCounter++, serverIpAddress);
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createDirectory", dir);
+ return dir;
+ }
+
+ public DirectoryNode createDirectory(final String nodeName) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createDirectory", nodeName);
+
+ // invoke overloaded method
+ final DirectoryNode dir =
+ this.createDirectory(nodeName, portCounter++, portCounter++,
+ portCounter++, portCounter++, "127.0.0.1");
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createDirectory", dir);
+ return dir;
+ }
+
+ public ProxyNode createProxy(final String nodeName, final int controlPort,
+ final int socksPort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createProxy", new Object[] {
+ nodeName, controlPort, socksPort });
+
+ // create proxy node; parameter checking is done in constructor
+ final ProxyNode proxy =
+ new ProxyNodeImpl(this, nodeName, controlPort, socksPort);
+
+ // add new proxy node to nodes collection
+ nodes.put(nodeName, proxy);
+
+ // add name to event manager as event source
+ eventManager.addEventSource(nodeName);
+
+ // log exiting and return proxy node
+ logger.exiting(this.getClass().getName(), "createProxy", proxy);
+ return proxy;
+ }
+
+ public ProxyNode createProxy(final String nodeName) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createProxy", nodeName);
+
+ // invoke overloaded method
+ final ProxyNode proxy =
+ this.createProxy(nodeName, portCounter++, portCounter++);
+
+ // log exiting and return proxy node
+ logger.exiting(this.getClass().getName(), "createProxy", proxy);
+ return proxy;
+ }
+
+ public RouterNode createRouter(final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort, final String serverIpAddress) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createRouter",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort, serverIpAddress });
+
+ // create router node; parameter checking is done in constructor
+ final RouterNode router =
+ new RouterNodeImpl(this, nodeName, controlPort, socksPort,
+ orPort, dirPort, serverIpAddress);
+
+ // add new router node to nodes collection
+ nodes.put(nodeName, router);
+
+ // add name to event manager as event source
+ eventManager.addEventSource(nodeName);
+
+ // log exiting and return router node
+ logger.exiting(this.getClass().getName(), "createRouter", router);
+ return router;
+ }
+
+ public RouterNode createRouter(final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createRouter",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort });
+
+ // invoke overloaded method
+ final DirectoryNode dir =
+ this.createDirectory(nodeName, controlPort, socksPort, orPort,
+ dirPort, "127.0.0.1");
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createRouter", dir);
+ return dir;
+ }
+
+ public RouterNode createRouter(final String nodeName,
+ final String serverIpAddress) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createRouter",
+ new Object[] { nodeName, serverIpAddress });
+
+ // invoke overloaded method
+ final RouterNode dir =
+ this.createRouter(nodeName, portCounter++, portCounter++,
+ portCounter++, portCounter++, serverIpAddress);
+
+ // log exiting and return directory node
+ logger.exiting(this.getClass().getName(), "createRouter", dir);
+ return dir;
+ }
+
+ public RouterNode createRouter(final String nodeName) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createRouter", nodeName);
+
+ // invoke overloaded method
+ final RouterNode router =
+ this.createRouter(nodeName, portCounter++, portCounter++,
+ portCounter++, portCounter++, "127.0.0.1");
+
+ // log exiting and return router node
+ logger.exiting(this.getClass().getName(), "createRouter", router);
+ return router;
+ }
+
+ public ServerApplication createServer(final String serverApplicationName,
+ final int serverPort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createServer",
+ new Object[] { serverApplicationName, serverPort });
+
+ // create server; parameter checking is done in constructor
+ final ServerApplicationImpl server =
+ new ServerApplicationImpl(this, serverApplicationName,
+ serverPort);
+
+ // add name to event manager as event source
+ eventManager.addEventSource(serverApplicationName);
+
+ // log exiting and return server
+ logger.exiting(this.getClass().getName(), "createServer", server);
+ return server;
+ }
+
+ public ServerApplication createServer(final String serverApplicationName) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "createServer",
+ serverApplicationName);
+
+ // invoke overloaded method
+ final ServerApplication server =
+ this.createServer(serverApplicationName, portCounter++);
+
+ // log exiting and return server
+ logger.exiting(this.getClass().getName(), "createServer", server);
+ return server;
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ /**
+ * Returns the implementation instance of the event manager of this network.
+ *
+ * @return The implementation instance of the event manager of this network.
+ */
+ public EventManagerImpl getEventManagerImpl() {
+ return eventManager;
+ }
+
+ public File getWorkingDirectory() {
+ return workingDir;
+ }
+
+ public ProxyNode getNode(final String nodeName) {
+ return nodes.get(nodeName);
+ }
+
+ public Map<String, ProxyNode> getAllProxyNodes() {
+ final Map<String, ProxyNode> result = new HashMap<String, ProxyNode>();
+ for (final String nodeName : nodes.keySet()) {
+ final ProxyNode node = nodes.get(nodeName);
+ if (!(node instanceof RouterNode)) {
+ result.put(nodeName, node);
+ }
+ }
+ return result;
+ }
+
+ public Map<String, RouterNode> getAllRouterNodes() {
+ final Map<String, RouterNode> result =
+ new HashMap<String, RouterNode>();
+ for (final String nodeName : nodes.keySet()) {
+ final ProxyNode node = nodes.get(nodeName);
+ if (node instanceof RouterNode && !(node instanceof DirectoryNode)) {
+ result.put(nodeName, (RouterNode) node);
+ }
+ }
+ return result;
+ }
+
+ public Map<String, DirectoryNode> getAllDirectoryNodes() {
+ final Map<String, DirectoryNode> result =
+ new HashMap<String, DirectoryNode>();
+ for (final String nodeName : nodes.keySet()) {
+ final ProxyNode node = nodes.get(nodeName);
+ if (node instanceof DirectoryNode) {
+ result.put(nodeName, (DirectoryNode) node);
+ }
+ }
+ return result;
+ }
+
+ public Map<String, ProxyNode> getAllNodes() {
+ return new HashMap<String, ProxyNode>(nodes);
+ }
+
+ public boolean hupUntilUp(final int tries, final long hupInterval)
+ throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "hupUntilUp", new Object[] {
+ tries, hupInterval });
+
+ // check if all nodes are running
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() != NodeState.RUNNING) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "All nodes must be running before sending them HUP "
+ + "commands!");
+ logger.throwing(this.getClass().getName(), "hupUntilUp", e);
+ throw e;
+ }
+ }
+
+ // check if nodes are already up; if so, return immediately
+ if (allNodesUp()) {
+
+ // log exiting and return true
+ logger.exiting(this.getClass().getName(), "hupUntilUp", true);
+ return true;
+ }
+
+ final Object hupLock = new Object();
+ // create and register a new event handler for each node
+ for (final ProxyNode node : nodes.values()) {
+ eventManager.addEventListener(node.getNodeName(),
+ new EventListener() {
+ public void handleEvent(final Event event) {
+ if (event.getType() == NodeEventType.NODE_CIRCUIT_OPENED) {
+ synchronized(hupLock) {
+ hupLock.notify();
+ }
+ eventManager.removeEventListener(this);
+ }
+ }
+ });
+ }
+
+ // walk through wait-check-hup loop until there are no tries left
+ for (int i = 0; i < tries; i++) {
+
+ // determine how long to try waiting for the hangup
+ final long endOfSleeping = System.currentTimeMillis() + hupInterval;
+
+ // unless all nodes have reported to be up, wait for the given
+ // maximum time
+ while (System.currentTimeMillis() < endOfSleeping) {
+
+ synchronized(hupLock) {
+ // check if nodes are up now
+ if (allNodesUp()) {
+
+ // log exiting and return true
+ logger.exiting(this.getClass().getName(), "hupUntilUp",
+ true);
+ return true;
+ }
+ // sleep
+ try {
+ hupLock.wait(hupInterval);
+ } catch (final InterruptedException e) {
+ // do nothing about it
+ }
+ }
+ }
+
+ logger.log(Level.FINE, "Sending HUP to nodes");
+ // send a HUP signal to all nodes
+ for (final ProxyNode node : nodes.values()) {
+ logger
+ .log(Level.FINE, "Sending HUP to node "
+ + node.toString());
+ node.hup();
+ }
+
+ // continue in loop
+ }
+
+ // no retries left and not all nodes are up; log exiting and return
+ // failure
+ logger.exiting(this.getClass().getName(), "hupUntilUp", false);
+ return false;
+ }
+
+ public void hupAllNodes() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "hupAllNodes");
+
+ // check if all nodes are running
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() != NodeState.RUNNING) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "All nodes must be running before sending them HUP "
+ + "commands!");
+ logger.throwing(this.getClass().getName(), "hupAllNodes", e);
+ throw e;
+ }
+ }
+
+ // send a HUP signal to all nodes
+ for (final ProxyNode node : nodes.values()) {
+ logger.log(Level.FINE, "Sending HUP to node " + node.toString());
+ node.hup();
+ }
+
+ // no retries left and not all nodes are up; log exiting and return
+ // failure
+ logger.exiting(this.getClass().getName(), "hupAllNodes");
+ }
+
+ public void hupAllDirectories() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "hupAllDirectories");
+
+ // check if all directory nodes are running
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof DirectoryNode
+ && node.getNodeState() != NodeState.RUNNING) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "All directory nodes must be running before sending "
+ + "them HUP commands!");
+ logger.throwing(this.getClass().getName(), "hupAllDirectories",
+ e);
+ throw e;
+ }
+ }
+
+ // send a HUP signal to all nodes
+ for (final ProxyNode node : nodes.values()) {
+ if (node instanceof DirectoryNode) {
+ logger
+ .log(Level.FINE, "Sending HUP to node "
+ + node.toString());
+ node.hup();
+ }
+ }
+
+ // no retries left and not all nodes are up; log exiting and return
+ // failure
+ logger.exiting(this.getClass().getName(), "hupAllDirectories");
+ }
+
+ public void shutdownNodes() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "shutdownNodes");
+
+ // iteratively shut down all running nodes; if an exception is caught,
+ // continue shutting down the other nodes and throw the first exception
+ // subsequently
+ PuppeTorException firstCaughtException = null;
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() == NodeState.RUNNING) {
+ try {
+ node.shutdown();
+ } catch (final PuppeTorException e) {
+ if (firstCaughtException == null) {
+ firstCaughtException = e;
+ }
+ }
+ }
+ }
+
+ // if an exception was caught during shutting down nodes, throw the
+ // first caught exception
+ if (firstCaughtException != null) {
+ logger.throwing(this.getClass().getName(), "shutdownNodes",
+ firstCaughtException);
+ throw firstCaughtException;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "shutdownNodes");
+ }
+
+ public boolean startNodes(final long maximumTimeToWaitInMillis)
+ throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "startNodes",
+ maximumTimeToWaitInMillis);
+
+ // check node states
+ for (final ProxyNode node : nodes.values()) {
+ if (node.getNodeState() != NodeState.CONFIGURATION_WRITTEN) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "All configurations must be written before starting "
+ + "nodes!");
+ logger.throwing(this.getClass().getName(), "startNodes", e);
+ throw e;
+ }
+ }
+
+ // check parameter
+ if (maximumTimeToWaitInMillis < 0) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "startNodes", e);
+ throw e;
+ }
+
+ // remember time when we begin starting the nodes
+ final long before = System.currentTimeMillis();
+
+ // start nodes in parallel
+ final Set<NodeStarter> allNodeStarters = new HashSet<NodeStarter>();
+ for (final ProxyNode node : nodes.values()) {
+ final NodeStarter nodeStarter =
+ new NodeStarter(node, maximumTimeToWaitInMillis);
+ allNodeStarters.add(nodeStarter);
+ nodeStarter.start();
+ }
+
+ // wait for all node starts to complete
+ for (final NodeStarter nodeStarter : allNodeStarters) {
+
+ // join node starts one after the other
+ try {
+ nodeStarter.join();
+ } catch (final InterruptedException e) {
+ // this happens?! we have some kind of problem here!
+ logger.log(Level.WARNING,
+ "Interrupt while joining node starter!");
+
+ // log exiting and return false
+ logger.exiting(this.getClass().getName(), "startNodes", false);
+ return false;
+ }
+
+ // if any thread has caught an exception, throw that exception now
+ final Exception caughtException = nodeStarter.caughtException;
+ if (caughtException != null) {
+ final PuppeTorException ex =
+ new PuppeTorException("Exception while starting node "
+ + nodeStarter.node.getNodeName(),
+ caughtException);
+ logger.throwing(this.getClass().getName(), "startNodes", ex);
+ throw ex;
+ }
+
+ // if node start did not succeed in the given time, fail
+ if (!nodeStarter.success) {
+ logger.log(Level.WARNING,
+ "Starting nodes was not successful in "
+ + maximumTimeToWaitInMillis / 1000
+ + " seconds.", networkName);
+
+ // log exiting and return false
+ logger.exiting(this.getClass().getName(), "startNodes", false);
+ return false;
+ }
+ }
+
+ // determine how long we took to start all nodes
+ final long after = System.currentTimeMillis();
+ logger.log(Level.FINE, "Starting nodes was successful and took "
+ + (after - before) / 1000 + " seconds.", networkName);
+
+ // log exiting and return true
+ logger.exiting(this.getClass().getName(), "startNodes", true);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": networkName=\""
+ + networkName;
+ }
+
+ public String getNetworkName() {
+ return networkName;
+ }
+
+ public void writeConfigurations() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "writeConfigurations");
+
+ // write configurations for all nodes
+ for (final ProxyNode node : nodes.values()) {
+ node.writeConfiguration();
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "writeConfigurations");
+ }
+
+ public void addTemplateConfiguration(
+ final Class<? extends ProxyNode> nodeClass,
+ final String templateConfigurationString) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addTemplateConfiguration",
+ new Object[] { nodeClass, templateConfigurationString });
+
+ // check parameters
+ if (nodeClass == null || templateConfigurationString == null
+ || templateConfigurationString.length() < 1
+ || !templateConfigurationString.contains(" ")) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "addTemplateConfiguration", e);
+ throw e;
+ }
+
+ // add template string to appropriate template configuration
+ if (nodeClass == ProxyNode.class) {
+ ProxyNodeImpl.templateConfiguration
+ .add(templateConfigurationString);
+ } else if (nodeClass == RouterNode.class) {
+ RouterNodeImpl.templateConfiguration
+ .add(templateConfigurationString);
+ } else if (nodeClass == DirectoryNode.class) {
+ DirectoryNodeImpl.templateConfiguration
+ .add(templateConfigurationString);
+ } else {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "addTemplateConfiguration", e);
+ throw e;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "addTemplateConfiguration");
+ }
+
+ public List<String> getTemplateConfiguration(
+ final Class<? extends ProxyNode> nodeClass) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "getTemplateConfiguration",
+ nodeClass);
+
+ // check parameter
+ if (nodeClass == null) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "getTemplateConfiguration", e);
+ throw e;
+ }
+
+ // obtain reference on appropriate template configuration
+ List<String> result = null;
+ if (nodeClass == ProxyNode.class) {
+ result = new ArrayList<String>(ProxyNodeImpl.templateConfiguration);
+ } else if (nodeClass == RouterNode.class) {
+ result =
+ new ArrayList<String>(RouterNodeImpl.templateConfiguration);
+ } else if (nodeClass == DirectoryNode.class) {
+ result =
+ new ArrayList<String>(
+ DirectoryNodeImpl.templateConfiguration);
+ } else {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "getTemplateConfiguration", e);
+ throw e;
+ }
+
+ // log exiting and return result
+ logger.exiting(this.getClass().getName(), "getTemplateConfiguration",
+ result);
+ return result;
+ }
+
+ public void removeTemplateConfiguration(
+ final Class<? extends ProxyNode> nodeClass,
+ final String templateConfigurationKey) {
+
+ // log entering
+ logger.entering(this.getClass().getName(),
+ "removeTemplateConfiguration", new Object[] { nodeClass,
+ templateConfigurationKey });
+
+ // check parameters
+ if (nodeClass == null || templateConfigurationKey == null
+ || templateConfigurationKey.length() < 1) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "removeTemplateConfiguration", e);
+ throw e;
+ }
+
+ // obtain reference on appropriate template configuration
+ List<String> templateConfig = null;
+ if (nodeClass == ProxyNode.class) {
+ templateConfig = ProxyNodeImpl.templateConfiguration;
+ } else if (nodeClass == RouterNode.class) {
+ templateConfig = RouterNodeImpl.templateConfiguration;
+ } else if (nodeClass == DirectoryNode.class) {
+ templateConfig = DirectoryNodeImpl.templateConfiguration;
+ } else {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(),
+ "removeTemplateConfiguration", e);
+ throw e;
+ }
+
+ // iterate over existing template configuration strings and remove all
+ // configuration strings that have the given configuration key
+ final List<String> configurationStringsToRemove =
+ new ArrayList<String>();
+ for (final String currentConfigurationString : templateConfig) {
+ final String currentConfigurationKey =
+ currentConfigurationString.substring(0,
+ currentConfigurationString.indexOf(" "));
+ if (currentConfigurationKey.equals(templateConfigurationKey)) {
+ configurationStringsToRemove.add(currentConfigurationString);
+ }
+ }
+ templateConfig.removeAll(configurationStringsToRemove);
+
+ // log exiting
+ logger
+ .exiting(this.getClass().getName(),
+ "removeTemplateConfiguration");
+ }
+
+ /**
+ * Returns the current port number and increments it afterwards.
+ *
+ * @return The current port number.
+ */
+ int getNextPortNumber() {
+ return portCounter++;
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
old mode 100755
new mode 100644
index 76cf436..1549472
--- a/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
@@ -1,760 +1,760 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.freehaven.tor.control.TorControlConnection;
-import de.uniba.wiai.lspi.puppetor.HiddenService;
-import de.uniba.wiai.lspi.puppetor.NodeEventType;
-import de.uniba.wiai.lspi.puppetor.NodeState;
-import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-
-/**
- * Implementation of <code>ProxyNode</code>.
- *
- * @author kloesing
- */
-public class ProxyNodeImpl implements ProxyNode {
-
- /**
- * Executable file containing Tor.
- *
- * TODO make this configurable!
- */
- protected static final File torExecutable = new File("tor");
-
- /**
- * The <code>torrc</code> configuration file of this Tor node.
- */
- protected File configFile;
-
- /**
- * Collects all configuration strings for this node during the configuration
- * phase in the order they are added.
- */
- protected List<String> configuration;
-
- /**
- * Connection via Tor controller.
- */
- protected TorControlConnection conn;
-
- /**
- * Port on which the Tor node will be listening for us as its controller.
- */
- protected int controlPort;
-
- /**
- * Event manager to which all events concerning this node are notified.
- */
- private final EventManagerImpl eventManager;
-
- /**
- * Logger for this node which is called "node." plus the name of this node.
- */
- protected Logger logger;
-
- /**
- * Network to which this node belongs.
- */
- protected NetworkImpl network;
-
- /**
- * Name of this node that is used as part of the working directory, as
- * logger name of this node, and as event source.
- */
- protected String nodeName;
-
- /**
- * The state of this node.
- */
- protected NodeState nodeState = NodeState.CONFIGURING;
-
- /**
- * Port on which the Tor node will be listening for SOCKS connection
- * requests.
- */
- protected int socksPort;
-
- /**
- * The running Tor process that belongs to this node.
- */
- protected Process torProcess;
-
- /**
- * Directory in which all information concerning this node is stored.
- */
- protected File workingDir;
-
- /**
- * Returns this node's working directory.
- *
- * @return This node's working directory.
- */
- File getWorkingDir() {
- return workingDir;
- }
-
- /**
- * Creates a new <code>ProxyNodeImpl</code> and adds it to the given
- * <code>network</code>, but does not yet write its configuration to disk
- * or start the corresponding Tor process.
- *
- * @param network
- * Network configuration to which this node belongs.
- * @param nodeName
- * The name of the new node which may only consist of between 1
- * and 19 alpha-numeric characters.
- * @param controlPort
- * Port on which the Tor node will be listening for us as its
- * controller. May not be negative or greater than 65535.
- * @param socksPort
- * Port on which the Tor node will be listening for SOCKS
- * connection requests. May not be negative or greater than
- * 65535.
- * @throws IllegalArgumentException
- * If at least one of the parameters is <code>null</code> or
- * has an invalid value.
- */
- ProxyNodeImpl(final NetworkImpl network, final String nodeName,
- final int controlPort, final int socksPort) {
-
- // make sure that nodeName is a valid logger name
- if (nodeName == null || nodeName.length() < 1 || nodeName.length() > 19
- || !nodeName.matches("[a-zA-Z0-9]*")) {
- final String reason =
- "\"" + nodeName + "\" is not a valid node name!";
- final IllegalArgumentException e =
- new IllegalArgumentException(reason);
- throw e;
- }
-
- // create logger
- logger = Logger.getLogger(nodeName + "." + this.getClass().getName());
-
- logger.setLevel(Level.ALL);
-
- // log entering
- logger.entering(this.getClass().getName(), "ProxyNodeImpl",
- new Object[] { network, nodeName, controlPort, socksPort });
-
- // check remaining parameters
- if (network == null || controlPort < 0 || controlPort > 65535
- || socksPort < 0 || socksPort > 65535) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "ProxyNodeImpl", e);
- throw e;
- }
-
- // store parameter values
- this.network = network;
- this.nodeName = nodeName;
- this.controlPort = controlPort;
- this.socksPort = socksPort;
-
- // obtain reference on event manager from network
- eventManager = network.getEventManagerImpl();
-
- // create working directory
- workingDir =
- new File(this.network.getWorkingDirectory().getAbsolutePath()
- + File.separator + nodeName + File.separator);
- workingDir.mkdirs();
- logger.log(Level.FINE, "Created working directory \""
- + workingDir.getAbsolutePath() + "\"");
-
- // create reference on config file
- configFile =
- new File(workingDir.getAbsolutePath() + File.separator
- + "torrc");
-
- // initialize configuration
- configuration = new ArrayList<String>(templateConfiguration);
- configuration.add("ControlPort " + controlPort);
- configuration.add("SocksPort " + socksPort);
-
- // initialize state
- nodeState = NodeState.CONFIGURING;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "ProxyNodeImpl");
- }
-
- public void addConfiguration(final String configurationString) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addConfiguration",
- configurationString);
-
- // check parameter
- if (configurationString == null || configurationString.length() < 1
- || !configurationString.contains(" ")) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "addConfiguration", e);
- throw e;
- }
-
- // add configuration string
- configuration.add(configurationString);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "addConfiguration");
- }
-
- public void addConfigurations(final List<String> configurationStrings) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addConfigurations",
- configurationStrings);
-
- // check parameter
- if (configurationStrings == null) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "addConfigurations", e);
- throw e;
- }
-
- // add configuration strings one by one
- for (final String conf : configurationStrings) {
- addConfiguration(conf);
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "addConfigurations");
- }
-
- public void replaceConfiguration(final String configurationString) {
-
- // log entering
- logger.entering(this.getClass().getName(), "replaceConfiguration",
- configurationString);
-
- // check parameter
- if (configurationString == null || configurationString.length() < 1
- || !configurationString.contains(" ")) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "replaceConfiguration",
- e);
- throw e;
- }
-
- // extract configuration key
- final String configurationKey =
- configurationString.substring(0, configurationString
- .indexOf(" "));
-
- // iterate over existing configuration strings and replace the first
- // occurrence of configuration key with new configuration string
- final Iterator<String> it = configuration.listIterator();
- boolean replaced = false;
- for (int counter = 0; !replaced && it.hasNext(); counter++) {
- final String currentConfigurationString = it.next();
- final String currentConfigurationKey =
- currentConfigurationString.substring(0,
- currentConfigurationString.indexOf(" "));
- if (currentConfigurationKey.equals(configurationKey)) {
- configuration.set(counter, configurationString);
- replaced = true;
- }
- }
-
- // if no such configuration key was found, append the configuration
- // string
- if (!replaced) {
- configuration.add(configurationString);
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "replaceConfiguration");
- }
-
- public void removeConfiguration(final String configurationKey) {
-
- // log entering
- logger.entering(this.getClass().getName(), "deleteConfiguration",
- configurationKey);
-
- // check parameter
- if (configurationKey == null || configurationKey.length() < 1) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger
- .throwing(this.getClass().getName(), "deleteConfiguration",
- e);
- throw e;
- }
-
- // iterate over existing configuration strings and remove all
- // configuration strings that have the given configuration key
- final List<String> configurationStringsToRemove =
- new ArrayList<String>();
- for (final String currentConfigurationString : configuration) {
- final String currentConfigurationKey =
- currentConfigurationString.substring(0,
- currentConfigurationString.indexOf(" "));
- if (currentConfigurationKey.equals(configurationKey)) {
- configurationStringsToRemove.add(currentConfigurationString);
- }
- }
- configuration.removeAll(configurationStringsToRemove);
-
- // log exiting
- logger.exiting(this.getClass().getName(), "deleteConfiguration");
- }
-
- public synchronized HiddenService addHiddenService(
- final String serviceName, final int servicePort,
- final int virtualPort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addHiddenService",
- new Object[] { serviceName, servicePort, virtualPort });
-
- // create hidden service object; parameter checking is done in
- // constructor
- final HiddenService result =
- new HiddenServiceImpl(this, serviceName, servicePort,
- virtualPort);
-
- // add hidden service using Tor controller
- configuration.add("HiddenServiceDir " + workingDir.getAbsolutePath()
- + File.separator + serviceName + "\nHiddenServicePort "
- + virtualPort + " 127.0.0.1:" + servicePort);
-
- // log exiting and return hidden service object
- logger.exiting(this.getClass().getName(), "addHiddenService", result);
- return result;
- }
-
- public synchronized HiddenService addHiddenService(
- final String serviceName, final int servicePort) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addHiddenService",
- new Object[] { serviceName, servicePort });
-
- // invoke overloaded method
- final HiddenService result =
- this.addHiddenService(serviceName, servicePort, 80);
-
- // log exiting and return hidden service
- logger.exiting(this.getClass().getName(), "addHiddenService", result);
- return result;
- }
-
- public synchronized HiddenService addHiddenService(final String serviceName) {
-
- // log entering
- logger.entering(this.getClass().getName(), "addHiddenService",
- serviceName);
-
- // invoke overloaded method
- final HiddenService result =
- this.addHiddenService(serviceName, network.getNextPortNumber(),
- 80);
-
- // log exiting and return hidden service
- logger.exiting(this.getClass().getName(), "addHiddenService", result);
- return result;
- }
-
- public String getNodeName() {
- return nodeName;
- }
-
- public synchronized NodeState getNodeState() {
- return nodeState;
- }
-
- public synchronized void hup() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "hup");
-
- // check state
- if (nodeState != NodeState.RUNNING || conn == null) {
- final IllegalStateException e =
- new IllegalStateException(
- "Cannot hup a process when it's not running or there is "
- + "no connection to its control port!");
- logger.throwing(this.getClass().getName(), "hup", e);
- throw e;
- }
-
- // send HUP signal to Tor process
- try {
- conn.signal("HUP");
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not send the command HUP to the Tor process!",
- e);
- logger.throwing(this.getClass().getName(), "hup", ex);
- throw ex;
- }
- // log exiting
- logger.exiting(this.getClass().getName(), "hup");
- }
-
- public synchronized void shutdown() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "shutdown");
-
- // check state
- if (nodeState != NodeState.RUNNING) {
- final IllegalStateException e = new IllegalStateException();
- logger.throwing(this.getClass().getName(), "shutdown", e);
- throw e;
- }
-
- // we cannot simply kill the Tor process, because we have established a
- // controller connection to it which would interpret a closed socket as
- // failure and throw a RuntimeException
- try {
- conn.shutdownTor("SHUTDOWN");
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not send shutdown command to Tor process!",
- e);
- logger.throwing(this.getClass().getName(), "shutdown", ex);
- throw ex;
- }
-
- // change state
- nodeState = NodeState.SHUT_DOWN;
-
- // fire event
- eventManager.observeInternalEvent(System.currentTimeMillis(),
- getNodeName(), NodeEventType.NODE_STOPPED, "Node stopped.");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "shutdown");
- }
-
- /**
- * Helper thread that waits for a given time for a given process to
- * potentially terminate in order to find out if there are problems. If
- * either the process terminates cleanly within this timeout, or does not
- * terminate, the exit value will be 0; otherwise it will contain the exit
- * code of the terminated process. This functionality is added, because it
- * is not provided by Process. XXX Some stuff in here looks still dodgy.
- * What happens if we get an InterruptedException in run and thus don't set
- * exitValue?-SH
- */
- private static class ProcessWaiter extends Thread {
-
- /** The process to wait for. */
- private final Process process;
-
- /** The exit value or 0 if the process is still running. */
- private final AtomicInteger exitValue;
-
- /**
- * Creates a new <code>ProcessWaiter</code> for process
- * <code>process</code>, but does not start it, yet.
- *
- * @param process
- * The process to wait for.
- */
- ProcessWaiter(final Process process) {
- this.process = process;
- exitValue = new AtomicInteger(0);
- }
-
- @Override
- public void run() {
- try {
- exitValue.set(process.waitFor());
- } catch (final InterruptedException e) {}
- }
-
- /**
- * Causes the current thread to wait until the process has terminated or
- * the <code>timeoutInMillis</code> has expired. This method returns
- * immediately if the subprocess has already terminated.
- *
- * @param timeoutInMillis
- * The maximum time to wait for the process to terminate.
- * @return The exit value of the terminated process or 0 if the process
- * is still running.
- */
- public int waitFor(final long timeoutInMillis) {
- try {
- sleep(timeoutInMillis);
- } catch (final InterruptedException e) {}
- interrupt();
- return exitValue.get();
- }
- }
-
- public synchronized boolean startNode(final long maximumTimeToWaitInMillis)
- throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "startNode",
- maximumTimeToWaitInMillis);
-
- // check state
- if (nodeState != NodeState.CONFIGURATION_WRITTEN) {
- final String reason =
- "Node is not in state "
- + "NodeState.CONFIGURATION_WRITTEN!";
- final IllegalStateException e = new IllegalStateException(reason);
- logger.throwing(this.getClass().getName(), "startNode", e);
- throw e;
- }
-
- // start process
- final ProcessBuilder processBuilder =
- new ProcessBuilder(torExecutable.getPath(), "-f", "torrc");
- processBuilder.directory(workingDir);
- processBuilder.redirectErrorStream(true);
- try {
- torProcess = processBuilder.start();
- logger.log(Level.FINE, "Started Tor process successfully!");
- } catch (final IOException e) {
- final String reason = "Could not start Tor process!";
- final PuppeTorException ex = new PuppeTorException(reason, e);
- logger.throwing(this.getClass().getName(), "startNode", ex);
- throw ex;
- }
-
- // start thread to parse output
- final BufferedReader br =
- new BufferedReader(new InputStreamReader(torProcess
- .getInputStream()));
- final Thread outputThread = new Thread() {
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- // read output from Tor to parse it
- String line = null;
- try {
- while ((line = br.readLine()) != null) {
- eventManager.observeUnparsedEvent(ProxyNodeImpl.this
- .getNodeName(), line);
- }
- } catch (IOException e) {
-
- // only print out a warning for this exception if this node
- // is running; otherwise, silently ignore it...
- if (getNodeState() == NodeState.RUNNING) {
- String reason =
- "IOException when reading output from Tor "
- + "process "
- + ProxyNodeImpl.this.getNodeName()
- + "!";
- logger.log(Level.WARNING, reason, e);
- }
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- };
- outputThread.setDaemon(true);
- outputThread.setName(nodeName + " Output Parser");
- outputThread.start();
- logger.log(Level.FINE, "Started thread to parse output!");
-
- // add shutdown hook that kills the process on JVM exit
- final Process p = torProcess;
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- // destroy Tor process
- p.destroy();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- });
- logger.log(Level.FINER,
- "Started shutdown hook that will destroy the Tor process on "
- + "JVM exit!");
-
- // wait to see if the process is started or exited immediately; wait for
- // one second to be sure that Tor terminates if there is an error,
- // especially if the computer is very busy and many nodes are created
- final ProcessWaiter waiter = new ProcessWaiter(torProcess);
- waiter.start();
- final int exitValue = waiter.waitFor(1000);
- if (exitValue != 0) {
- // Tor did not manage to start correctly
- logger.log(Level.WARNING, "Could not start Tor process! Tor "
- + "exited with exit value " + exitValue
- + "! Please go check the config options in " + configFile
- + " manually!");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "startNode", false);
- return false;
- }
-
- // wait for Tor to open the control port
- logger.log(Level.FINER, "Waiting for Tor to open its control port...");
- if (!eventManager.waitForAnyOccurence(nodeName,
- NodeEventType.NODE_CONTROL_PORT_OPENED,
- maximumTimeToWaitInMillis)) {
-
- // Tor did not open its control port
- logger.log(Level.WARNING, "Tor node " + nodeName
- + " did not manage to open its control port within "
- + maximumTimeToWaitInMillis + " milliseconds!");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "startNode", false);
- return false;
- }
- logger.log(Level.FINE,
- "Tor has successfully opened its control port and told us "
- + "about that!");
-
- // connect to the controller
- logger.log(Level.FINER, "Connecting to control port...");
- try {
- final Socket controlSocket =
- new java.net.Socket("127.0.0.1", controlPort);
- conn = TorControlConnection.getConnection(controlSocket);
- conn.authenticate(new byte[0]);
- } catch (final IOException e) {
- final String reason =
- "Could not connect to control port " + controlPort + "!";
- final PuppeTorException ex = new PuppeTorException(reason, e);
- logger.throwing(this.getClass().getName(), "startNode", ex);
- throw ex;
- }
- logger.log(Level.FINE, "Connected to control port successfully!");
-
- // set state to RUNNING
- nodeState = NodeState.RUNNING;
-
- // fire event
- eventManager.observeInternalEvent(System.currentTimeMillis(),
- getNodeName(), NodeEventType.NODE_STARTED, "Node started.");
-
- // log exiting and return with success
- logger.exiting(this.getClass().getName(), "startNode", true);
- return true;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": nodeName=\"" + nodeName
- + "\", controlPort=" + controlPort + ", socksPort=" + socksPort;
- }
-
- public synchronized void writeConfiguration() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "writeConfiguration");
-
- // write config file
- try {
- final BufferedWriter bw =
- new BufferedWriter(new FileWriter(configFile));
- for (final String c : configuration) {
- bw.write(c + "\n");
- }
- bw.close();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not write configuration file!", e);
- logger.throwing(this.getClass().getName(),
- "writeConfigurationFile", ex);
- throw ex;
- }
-
- // change state, if necessary
- if (nodeState == NodeState.CONFIGURING) {
- nodeState = NodeState.CONFIGURATION_WRITTEN;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "writeConfiguration");
- }
-
- public int getSocksPort() {
- return socksPort;
- }
-
- public int getControlPort() {
- return controlPort;
- }
-
- public List<String> getConfiguration() {
- return new ArrayList<String>(configuration);
- }
-
- /**
- * Template configuration of proxy nodes.
- */
- static List<String> templateConfiguration;
-
- static {
- templateConfiguration = new ArrayList<String>();
-
- templateConfiguration.add("DataDirectory .");
- templateConfiguration.add("SafeLogging 0");
- templateConfiguration.add("UseEntryGuards 0");
-
- templateConfiguration.add("Log info stdout");
- templateConfiguration.add("Log info file log");
-
- // TODO This is now contained in proposal 135.
- // templateConfiguration.add("EnforceDistinctSubnets 0");
- // templateConfiguration.add("ClientDNSRejectInternalAddresses 0");
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.freehaven.tor.control.TorControlConnection;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.NodeEventType;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Implementation of <code>ProxyNode</code>.
+ *
+ * @author kloesing
+ */
+public class ProxyNodeImpl implements ProxyNode {
+
+ /**
+ * Executable file containing Tor.
+ *
+ * TODO make this configurable!
+ */
+ protected static final File torExecutable = new File("tor");
+
+ /**
+ * The <code>torrc</code> configuration file of this Tor node.
+ */
+ protected File configFile;
+
+ /**
+ * Collects all configuration strings for this node during the configuration
+ * phase in the order they are added.
+ */
+ protected List<String> configuration;
+
+ /**
+ * Connection via Tor controller.
+ */
+ protected TorControlConnection conn;
+
+ /**
+ * Port on which the Tor node will be listening for us as its controller.
+ */
+ protected int controlPort;
+
+ /**
+ * Event manager to which all events concerning this node are notified.
+ */
+ private final EventManagerImpl eventManager;
+
+ /**
+ * Logger for this node which is called "node." plus the name of this node.
+ */
+ protected Logger logger;
+
+ /**
+ * Network to which this node belongs.
+ */
+ protected NetworkImpl network;
+
+ /**
+ * Name of this node that is used as part of the working directory, as
+ * logger name of this node, and as event source.
+ */
+ protected String nodeName;
+
+ /**
+ * The state of this node.
+ */
+ protected NodeState nodeState = NodeState.CONFIGURING;
+
+ /**
+ * Port on which the Tor node will be listening for SOCKS connection
+ * requests.
+ */
+ protected int socksPort;
+
+ /**
+ * The running Tor process that belongs to this node.
+ */
+ protected Process torProcess;
+
+ /**
+ * Directory in which all information concerning this node is stored.
+ */
+ protected File workingDir;
+
+ /**
+ * Returns this node's working directory.
+ *
+ * @return This node's working directory.
+ */
+ File getWorkingDir() {
+ return workingDir;
+ }
+
+ /**
+ * Creates a new <code>ProxyNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The name of the new node which may only consist of between 1
+ * and 19 alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ ProxyNodeImpl(final NetworkImpl network, final String nodeName,
+ final int controlPort, final int socksPort) {
+
+ // make sure that nodeName is a valid logger name
+ if (nodeName == null || nodeName.length() < 1 || nodeName.length() > 19
+ || !nodeName.matches("[a-zA-Z0-9]*")) {
+ final String reason =
+ "\"" + nodeName + "\" is not a valid node name!";
+ final IllegalArgumentException e =
+ new IllegalArgumentException(reason);
+ throw e;
+ }
+
+ // create logger
+ logger = Logger.getLogger(nodeName + "." + this.getClass().getName());
+
+ logger.setLevel(Level.ALL);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "ProxyNodeImpl",
+ new Object[] { network, nodeName, controlPort, socksPort });
+
+ // check remaining parameters
+ if (network == null || controlPort < 0 || controlPort > 65535
+ || socksPort < 0 || socksPort > 65535) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "ProxyNodeImpl", e);
+ throw e;
+ }
+
+ // store parameter values
+ this.network = network;
+ this.nodeName = nodeName;
+ this.controlPort = controlPort;
+ this.socksPort = socksPort;
+
+ // obtain reference on event manager from network
+ eventManager = network.getEventManagerImpl();
+
+ // create working directory
+ workingDir =
+ new File(this.network.getWorkingDirectory().getAbsolutePath()
+ + File.separator + nodeName + File.separator);
+ workingDir.mkdirs();
+ logger.log(Level.FINE, "Created working directory \""
+ + workingDir.getAbsolutePath() + "\"");
+
+ // create reference on config file
+ configFile =
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "torrc");
+
+ // initialize configuration
+ configuration = new ArrayList<String>(templateConfiguration);
+ configuration.add("ControlPort " + controlPort);
+ configuration.add("SocksPort " + socksPort);
+
+ // initialize state
+ nodeState = NodeState.CONFIGURING;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "ProxyNodeImpl");
+ }
+
+ public void addConfiguration(final String configurationString) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addConfiguration",
+ configurationString);
+
+ // check parameter
+ if (configurationString == null || configurationString.length() < 1
+ || !configurationString.contains(" ")) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "addConfiguration", e);
+ throw e;
+ }
+
+ // add configuration string
+ configuration.add(configurationString);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "addConfiguration");
+ }
+
+ public void addConfigurations(final List<String> configurationStrings) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addConfigurations",
+ configurationStrings);
+
+ // check parameter
+ if (configurationStrings == null) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "addConfigurations", e);
+ throw e;
+ }
+
+ // add configuration strings one by one
+ for (final String conf : configurationStrings) {
+ addConfiguration(conf);
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "addConfigurations");
+ }
+
+ public void replaceConfiguration(final String configurationString) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "replaceConfiguration",
+ configurationString);
+
+ // check parameter
+ if (configurationString == null || configurationString.length() < 1
+ || !configurationString.contains(" ")) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "replaceConfiguration",
+ e);
+ throw e;
+ }
+
+ // extract configuration key
+ final String configurationKey =
+ configurationString.substring(0, configurationString
+ .indexOf(" "));
+
+ // iterate over existing configuration strings and replace the first
+ // occurrence of configuration key with new configuration string
+ final Iterator<String> it = configuration.listIterator();
+ boolean replaced = false;
+ for (int counter = 0; !replaced && it.hasNext(); counter++) {
+ final String currentConfigurationString = it.next();
+ final String currentConfigurationKey =
+ currentConfigurationString.substring(0,
+ currentConfigurationString.indexOf(" "));
+ if (currentConfigurationKey.equals(configurationKey)) {
+ configuration.set(counter, configurationString);
+ replaced = true;
+ }
+ }
+
+ // if no such configuration key was found, append the configuration
+ // string
+ if (!replaced) {
+ configuration.add(configurationString);
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "replaceConfiguration");
+ }
+
+ public void removeConfiguration(final String configurationKey) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "deleteConfiguration",
+ configurationKey);
+
+ // check parameter
+ if (configurationKey == null || configurationKey.length() < 1) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger
+ .throwing(this.getClass().getName(), "deleteConfiguration",
+ e);
+ throw e;
+ }
+
+ // iterate over existing configuration strings and remove all
+ // configuration strings that have the given configuration key
+ final List<String> configurationStringsToRemove =
+ new ArrayList<String>();
+ for (final String currentConfigurationString : configuration) {
+ final String currentConfigurationKey =
+ currentConfigurationString.substring(0,
+ currentConfigurationString.indexOf(" "));
+ if (currentConfigurationKey.equals(configurationKey)) {
+ configurationStringsToRemove.add(currentConfigurationString);
+ }
+ }
+ configuration.removeAll(configurationStringsToRemove);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "deleteConfiguration");
+ }
+
+ public synchronized HiddenService addHiddenService(
+ final String serviceName, final int servicePort,
+ final int virtualPort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addHiddenService",
+ new Object[] { serviceName, servicePort, virtualPort });
+
+ // create hidden service object; parameter checking is done in
+ // constructor
+ final HiddenService result =
+ new HiddenServiceImpl(this, serviceName, servicePort,
+ virtualPort);
+
+ // add hidden service using Tor controller
+ configuration.add("HiddenServiceDir " + workingDir.getAbsolutePath()
+ + File.separator + serviceName + "\nHiddenServicePort "
+ + virtualPort + " 127.0.0.1:" + servicePort);
+
+ // log exiting and return hidden service object
+ logger.exiting(this.getClass().getName(), "addHiddenService", result);
+ return result;
+ }
+
+ public synchronized HiddenService addHiddenService(
+ final String serviceName, final int servicePort) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addHiddenService",
+ new Object[] { serviceName, servicePort });
+
+ // invoke overloaded method
+ final HiddenService result =
+ this.addHiddenService(serviceName, servicePort, 80);
+
+ // log exiting and return hidden service
+ logger.exiting(this.getClass().getName(), "addHiddenService", result);
+ return result;
+ }
+
+ public synchronized HiddenService addHiddenService(final String serviceName) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "addHiddenService",
+ serviceName);
+
+ // invoke overloaded method
+ final HiddenService result =
+ this.addHiddenService(serviceName, network.getNextPortNumber(),
+ 80);
+
+ // log exiting and return hidden service
+ logger.exiting(this.getClass().getName(), "addHiddenService", result);
+ return result;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public synchronized NodeState getNodeState() {
+ return nodeState;
+ }
+
+ public synchronized void hup() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "hup");
+
+ // check state
+ if (nodeState != NodeState.RUNNING || conn == null) {
+ final IllegalStateException e =
+ new IllegalStateException(
+ "Cannot hup a process when it's not running or there is "
+ + "no connection to its control port!");
+ logger.throwing(this.getClass().getName(), "hup", e);
+ throw e;
+ }
+
+ // send HUP signal to Tor process
+ try {
+ conn.signal("HUP");
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not send the command HUP to the Tor process!",
+ e);
+ logger.throwing(this.getClass().getName(), "hup", ex);
+ throw ex;
+ }
+ // log exiting
+ logger.exiting(this.getClass().getName(), "hup");
+ }
+
+ public synchronized void shutdown() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "shutdown");
+
+ // check state
+ if (nodeState != NodeState.RUNNING) {
+ final IllegalStateException e = new IllegalStateException();
+ logger.throwing(this.getClass().getName(), "shutdown", e);
+ throw e;
+ }
+
+ // we cannot simply kill the Tor process, because we have established a
+ // controller connection to it which would interpret a closed socket as
+ // failure and throw a RuntimeException
+ try {
+ conn.shutdownTor("SHUTDOWN");
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not send shutdown command to Tor process!",
+ e);
+ logger.throwing(this.getClass().getName(), "shutdown", ex);
+ throw ex;
+ }
+
+ // change state
+ nodeState = NodeState.SHUT_DOWN;
+
+ // fire event
+ eventManager.observeInternalEvent(System.currentTimeMillis(),
+ getNodeName(), NodeEventType.NODE_STOPPED, "Node stopped.");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "shutdown");
+ }
+
+ /**
+ * Helper thread that waits for a given time for a given process to
+ * potentially terminate in order to find out if there are problems. If
+ * either the process terminates cleanly within this timeout, or does not
+ * terminate, the exit value will be 0; otherwise it will contain the exit
+ * code of the terminated process. This functionality is added, because it
+ * is not provided by Process. XXX Some stuff in here looks still dodgy.
+ * What happens if we get an InterruptedException in run and thus don't set
+ * exitValue?-SH
+ */
+ private static class ProcessWaiter extends Thread {
+
+ /** The process to wait for. */
+ private final Process process;
+
+ /** The exit value or 0 if the process is still running. */
+ private final AtomicInteger exitValue;
+
+ /**
+ * Creates a new <code>ProcessWaiter</code> for process
+ * <code>process</code>, but does not start it, yet.
+ *
+ * @param process
+ * The process to wait for.
+ */
+ ProcessWaiter(final Process process) {
+ this.process = process;
+ exitValue = new AtomicInteger(0);
+ }
+
+ @Override
+ public void run() {
+ try {
+ exitValue.set(process.waitFor());
+ } catch (final InterruptedException e) {}
+ }
+
+ /**
+ * Causes the current thread to wait until the process has terminated or
+ * the <code>timeoutInMillis</code> has expired. This method returns
+ * immediately if the subprocess has already terminated.
+ *
+ * @param timeoutInMillis
+ * The maximum time to wait for the process to terminate.
+ * @return The exit value of the terminated process or 0 if the process
+ * is still running.
+ */
+ public int waitFor(final long timeoutInMillis) {
+ try {
+ sleep(timeoutInMillis);
+ } catch (final InterruptedException e) {}
+ interrupt();
+ return exitValue.get();
+ }
+ }
+
+ public synchronized boolean startNode(final long maximumTimeToWaitInMillis)
+ throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "startNode",
+ maximumTimeToWaitInMillis);
+
+ // check state
+ if (nodeState != NodeState.CONFIGURATION_WRITTEN) {
+ final String reason =
+ "Node is not in state "
+ + "NodeState.CONFIGURATION_WRITTEN!";
+ final IllegalStateException e = new IllegalStateException(reason);
+ logger.throwing(this.getClass().getName(), "startNode", e);
+ throw e;
+ }
+
+ // start process
+ final ProcessBuilder processBuilder =
+ new ProcessBuilder(torExecutable.getPath(), "-f", "torrc");
+ processBuilder.directory(workingDir);
+ processBuilder.redirectErrorStream(true);
+ try {
+ torProcess = processBuilder.start();
+ logger.log(Level.FINE, "Started Tor process successfully!");
+ } catch (final IOException e) {
+ final String reason = "Could not start Tor process!";
+ final PuppeTorException ex = new PuppeTorException(reason, e);
+ logger.throwing(this.getClass().getName(), "startNode", ex);
+ throw ex;
+ }
+
+ // start thread to parse output
+ final BufferedReader br =
+ new BufferedReader(new InputStreamReader(torProcess
+ .getInputStream()));
+ final Thread outputThread = new Thread() {
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // read output from Tor to parse it
+ String line = null;
+ try {
+ while ((line = br.readLine()) != null) {
+ eventManager.observeUnparsedEvent(ProxyNodeImpl.this
+ .getNodeName(), line);
+ }
+ } catch (IOException e) {
+
+ // only print out a warning for this exception if this node
+ // is running; otherwise, silently ignore it...
+ if (getNodeState() == NodeState.RUNNING) {
+ String reason =
+ "IOException when reading output from Tor "
+ + "process "
+ + ProxyNodeImpl.this.getNodeName()
+ + "!";
+ logger.log(Level.WARNING, reason, e);
+ }
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ };
+ outputThread.setDaemon(true);
+ outputThread.setName(nodeName + " Output Parser");
+ outputThread.start();
+ logger.log(Level.FINE, "Started thread to parse output!");
+
+ // add shutdown hook that kills the process on JVM exit
+ final Process p = torProcess;
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // destroy Tor process
+ p.destroy();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ });
+ logger.log(Level.FINER,
+ "Started shutdown hook that will destroy the Tor process on "
+ + "JVM exit!");
+
+ // wait to see if the process is started or exited immediately; wait for
+ // one second to be sure that Tor terminates if there is an error,
+ // especially if the computer is very busy and many nodes are created
+ final ProcessWaiter waiter = new ProcessWaiter(torProcess);
+ waiter.start();
+ final int exitValue = waiter.waitFor(1000);
+ if (exitValue != 0) {
+ // Tor did not manage to start correctly
+ logger.log(Level.WARNING, "Could not start Tor process! Tor "
+ + "exited with exit value " + exitValue
+ + "! Please go check the config options in " + configFile
+ + " manually!");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "startNode", false);
+ return false;
+ }
+
+ // wait for Tor to open the control port
+ logger.log(Level.FINER, "Waiting for Tor to open its control port...");
+ if (!eventManager.waitForAnyOccurence(nodeName,
+ NodeEventType.NODE_CONTROL_PORT_OPENED,
+ maximumTimeToWaitInMillis)) {
+
+ // Tor did not open its control port
+ logger.log(Level.WARNING, "Tor node " + nodeName
+ + " did not manage to open its control port within "
+ + maximumTimeToWaitInMillis + " milliseconds!");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "startNode", false);
+ return false;
+ }
+ logger.log(Level.FINE,
+ "Tor has successfully opened its control port and told us "
+ + "about that!");
+
+ // connect to the controller
+ logger.log(Level.FINER, "Connecting to control port...");
+ try {
+ final Socket controlSocket =
+ new java.net.Socket("127.0.0.1", controlPort);
+ conn = TorControlConnection.getConnection(controlSocket);
+ conn.authenticate(new byte[0]);
+ } catch (final IOException e) {
+ final String reason =
+ "Could not connect to control port " + controlPort + "!";
+ final PuppeTorException ex = new PuppeTorException(reason, e);
+ logger.throwing(this.getClass().getName(), "startNode", ex);
+ throw ex;
+ }
+ logger.log(Level.FINE, "Connected to control port successfully!");
+
+ // set state to RUNNING
+ nodeState = NodeState.RUNNING;
+
+ // fire event
+ eventManager.observeInternalEvent(System.currentTimeMillis(),
+ getNodeName(), NodeEventType.NODE_STARTED, "Node started.");
+
+ // log exiting and return with success
+ logger.exiting(this.getClass().getName(), "startNode", true);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": nodeName=\"" + nodeName
+ + "\", controlPort=" + controlPort + ", socksPort=" + socksPort;
+ }
+
+ public synchronized void writeConfiguration() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "writeConfiguration");
+
+ // write config file
+ try {
+ final BufferedWriter bw =
+ new BufferedWriter(new FileWriter(configFile));
+ for (final String c : configuration) {
+ bw.write(c + "\n");
+ }
+ bw.close();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not write configuration file!", e);
+ logger.throwing(this.getClass().getName(),
+ "writeConfigurationFile", ex);
+ throw ex;
+ }
+
+ // change state, if necessary
+ if (nodeState == NodeState.CONFIGURING) {
+ nodeState = NodeState.CONFIGURATION_WRITTEN;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "writeConfiguration");
+ }
+
+ public int getSocksPort() {
+ return socksPort;
+ }
+
+ public int getControlPort() {
+ return controlPort;
+ }
+
+ public List<String> getConfiguration() {
+ return new ArrayList<String>(configuration);
+ }
+
+ /**
+ * Template configuration of proxy nodes.
+ */
+ static List<String> templateConfiguration;
+
+ static {
+ templateConfiguration = new ArrayList<String>();
+
+ templateConfiguration.add("DataDirectory .");
+ templateConfiguration.add("SafeLogging 0");
+ templateConfiguration.add("UseEntryGuards 0");
+
+ templateConfiguration.add("Log info stdout");
+ templateConfiguration.add("Log info file log");
+
+ // TODO This is now contained in proposal 135.
+ // templateConfiguration.add("EnforceDistinctSubnets 0");
+ // templateConfiguration.add("ClientDNSRejectInternalAddresses 0");
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
old mode 100755
new mode 100644
index 84a90e4..7a69ef7
--- a/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
@@ -1,428 +1,428 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.regex.Pattern;
-
-import de.uniba.wiai.lspi.puppetor.PuppeTorException;
-import de.uniba.wiai.lspi.puppetor.RouterNode;
-
-/**
- * Implementation of <code>RouterNode</code>.
- *
- * @author kloesing
- */
-public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
-
- /**
- * Internal thread class that is used to determine fingerprints in parallel,
- * which can take a few seconds.
- */
- private class FingerprintThread extends Thread {
-
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- // create file reference for temporary config file
- final File tempConfigFile =
- new File(workingDir.getAbsolutePath() + File.separator
- + "torrc.tmp");
-
- // compose a modified config file, including a DirServer option with
- // false fingerprint; this is necessary, because otherwise Tor
- // would not accept that this router node might have a private IP
- // address, but connects to the public directory servers
- final List<String> copyOfConfig =
- new ArrayList<String>(configuration);
- final String fakeDirServerString =
- "DirServer "
- + nodeName
- + " 127.0.0.1:"
- + dirPort
- + " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
- copyOfConfig.add(fakeDirServerString);
-
- // write config file
- try {
- final BufferedWriter bw =
- new BufferedWriter(new FileWriter(tempConfigFile));
- for (final String c : copyOfConfig) {
- bw.write(c + "\n");
- }
- bw.close();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not write configuration file!", e);
- logger.log(Level.WARNING, "Could not start Tor process!", ex);
- setCaughtException(ex);
- return;
- }
-
- // start process with option --list-fingerprint
- final ProcessBuilder processBuilder =
- new ProcessBuilder(torExecutable.getPath(),
- "--list-fingerprint", "-f", "torrc.tmp");
- processBuilder.directory(workingDir);
- processBuilder.redirectErrorStream(true);
- Process tmpProcess = null;
- try {
- tmpProcess = processBuilder.start();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not start Tor process temporarily with "
- + "--list-fingerprint option!", e);
- logger.log(Level.WARNING, "Could not start Tor process!", ex);
- setCaughtException(ex);
- return;
- }
-
- // wait for process to terminate
- int exitValue = 0;
- try {
- exitValue = tmpProcess.waitFor();
- } catch (final InterruptedException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Interrupted while waiting for Tor process to exit!",
- e);
- logger.log(Level.WARNING,
- "Temporary Tor process was interrupted!", ex);
- setCaughtException(ex);
- return;
- }
-
- if (exitValue != 0) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not start Tor process temporarily with "
- + "--list-fingerprint option! Tor exited with "
- + "exit value "
- + exitValue
- + "! Please go check the config options in "
- + tempConfigFile + " manually!");
- logger.log(Level.WARNING, "Could not start Tor process!", ex);
- setCaughtException(ex);
- return;
- }
-
- // read fingerprint from file
- final File fingerprintFile =
- new File(workingDir.getAbsolutePath() + File.separator
- + "fingerprint");
- try {
- final BufferedReader br2 =
- new BufferedReader(new FileReader(fingerprintFile));
- setFingerprint(br2.readLine());
- br2.close();
- } catch (final IOException e) {
- final PuppeTorException ex =
- new PuppeTorException(
- "Could not read fingerprint from file!", e);
- logger.log(Level.WARNING, "Could not read fingerprint file!",
- ex);
- setCaughtException(ex);
- return;
- }
-
- // delete temporary config file
- tempConfigFile.delete();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- }
-
- /**
- * Invoked by the fingerprint thread: sets the determined fingerprint string
- *
- * @param fingerprint
- * The determined fingerprint string.
- */
- private synchronized void setFingerprint(final String fingerprint) {
-
- // log entering
- logger.entering(this.getClass().getName(), "setFingerprint",
- fingerprint);
-
- // remember fingerprint and notify all waiting threads
- this.fingerprint = fingerprint;
- notifyAll();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "setFingerprint");
- }
-
- /**
- * Invoked by the fingerprint thread: sets the exception that occurred when
- * trying to determine the fingerprint.
- *
- * @param caughtException
- * The exception that occurred when trying to determine the
- * fingerprint.
- */
- protected synchronized void setCaughtException(
- final PuppeTorException caughtException) {
-
- // log entering
- logger.entering(this.getClass().getName(), "setCaughtException",
- caughtException);
-
- // remember caught exception and notify all waiting threads
- this.caughtException = caughtException;
- notifyAll();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "setCaughtException");
- }
-
- /**
- * Port on which the Tor node will be listening for directory requests from
- * other Tor nodes.
- */
- protected int dirPort;
-
- /**
- * The IP v4 address on which the node will listen in dotted decimal
- * notation.
- */
- protected String serverIpAddress;
-
- /**
- * The fingerprint of this node that is determined as hash value of its
- * onion key. It is initialized with <code>null</code> and set by the
- * fingerprint thread as soon as it is determined.
- */
- private String fingerprint;
-
- /**
- * The exception that was caught when determining the fingerprint of this
- * node, if any.
- */
- protected PuppeTorException caughtException;
-
- /**
- * The pattern for valid IP v4 addresses in dotted decimal notation.
- */
- private static final Pattern validIpAddressPattern =
- Pattern
- .compile("([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
- + "(\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){1,3}");
-
- /**
- * Port on which the Tor node will be listening for onion requests by other
- * Tor nodes.
- */
- protected int orPort;
-
- /**
- * Creates a new <code>RouterNodeImpl</code> and adds it to the given
- * <code>network</code>, but does not yet write its configuration to disk
- * or start the corresponding Tor process.
- *
- * @param network
- * Network configuration to which this node belongs.
- * @param nodeName
- * The name of the new node which may only consist of between 1
- * and 19 alpha-numeric characters.
- * @param controlPort
- * Port on which the Tor node will be listening for us as its
- * controller. May not be negative or greater than 65535.
- * @param socksPort
- * Port on which the Tor node will be listening for SOCKS
- * connection requests. May not be negative or greater than
- * 65535.
- * @param orPort
- * Port on which the Tor node will be listening for onion
- * requests by other Tor nodes. May not be negative or greater
- * than 65535.
- * @param dirPort
- * Port on which the Tor node will be listening for directory
- * requests from other Tor nodes. May not be negative or greater
- * than 65535.
- * @param serverIpAddress
- * The IP address on which the node will listen. Must be a valid
- * IP v4 address in dotted decimal notation. May not be
- * <code>null</code>.
- * @throws IllegalArgumentException
- * If at least one of the parameters is <code>null</code> or
- * has an invalid value.
- */
- RouterNodeImpl(final NetworkImpl network, final String nodeName,
- final int controlPort, final int socksPort, final int orPort,
- final int dirPort, final String serverIpAddress) {
-
- // create superclass instance; parameter checking is done in super
- // constructor
- super(network, nodeName, controlPort, socksPort);
-
- // log entering
- logger.entering(this.getClass().getName(), "RouterNodeImpl",
- new Object[] { network, nodeName, controlPort, socksPort,
- orPort, dirPort, serverIpAddress });
-
- // check parameters
- if (orPort < 0 || orPort > 65535 || dirPort < 0 || dirPort > 65535
- || serverIpAddress == null
- || !validIpAddressPattern.matcher(serverIpAddress).matches()) {
- final IllegalArgumentException e =
- new IllegalArgumentException("nodeName=" + nodeName
- + ", controlPort=" + controlPort + ", socksPort="
- + socksPort + ", orPort=" + orPort + ", dirPort="
- + dirPort + ", serverIpAddress='" + serverIpAddress
- + "'");
- logger.throwing(this.getClass().getName(), "RouterNodeImpl", e);
- throw e;
- }
-
- // remember parameters
- this.orPort = orPort;
- this.dirPort = dirPort;
- this.serverIpAddress = serverIpAddress;
-
- // extend configuration by template configuration of router nodes
- configuration.addAll(templateConfiguration);
-
- // add further configuration to make this node a router node
- configuration.add("ORPort " + orPort);
- configuration.add("Nickname " + nodeName);
-
- // all routers mirror the directory
- configuration.add("DirPort " + dirPort);
-
- // the address of this node should be manually specified and not guessed
- // by Tor
- configuration.add("Address " + serverIpAddress);
-
- // the OR port may only be contacted locally
- configuration.add("ORListenAddress " + serverIpAddress);
-
- // offer directory only locally (either by being an authority, or by
- // mirroring it)
- configuration.add("DirListenAddress " + serverIpAddress);
-
- // start a thread to determine the node's fingerprint in the background
- determineFingerprint();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "RouterNodeImpl");
- }
-
- public synchronized String getFingerprint() throws PuppeTorException {
-
- // log entering
- logger.entering(this.getClass().getName(), "getFingerprint");
-
- // wait until either the fingerprint has been determined or an exception
- // was caught
- while (fingerprint == null && caughtException == null) {
- try {
- wait();
- } catch (final InterruptedException e) {
- // do nothing
- }
- }
-
- if (caughtException != null) {
- logger.throwing(this.getClass().getName(), "getFingerprint",
- caughtException);
- throw caughtException;
- }
-
- // log exiting
- logger
- .exiting(this.getClass().getName(), "getFingerprint",
- fingerprint);
- return fingerprint;
- }
-
- @Override
- public String toString() {
- return super.toString() + ", orPort=" + orPort + ", dirPort=" + dirPort;
- }
-
- public int getDirPort() {
- return dirPort;
- }
-
- public int getOrPort() {
- return orPort;
- }
-
- /**
- * Determines the fingerprint of this node by starting a background thread
- * that performs this operation.
- */
- protected synchronized void determineFingerprint() {
-
- // log entering
- logger.entering(this.getClass().getName(), "determineFingerprint");
-
- // start a thread to determine this node's fingerprint
- final FingerprintThread fingerprintThread = new FingerprintThread();
- fingerprintThread.setName(nodeName + " Fingerprint Resolver");
- fingerprintThread.start();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "determineFingerprint");
- }
-
- /**
- * Template configuration of router nodes.
- */
- static List<String> templateConfiguration;
-
- static {
- templateConfiguration = new ArrayList<String>();
-
- templateConfiguration.add("ContactInfo wont@xxxxxxxxx");
- templateConfiguration.add("HidServDirectoryV2 1");
-
- // TODO This is now contained in proposal 135.
- // templateConfiguration.add("ExitPolicyRejectPrivate 0");
- // templateConfiguration.add("AssumeReachable 1");
- // templateConfiguration.add("ServerDNSAllowBrokenResolvConf 1");
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.regex.Pattern;
+
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+
+/**
+ * Implementation of <code>RouterNode</code>.
+ *
+ * @author kloesing
+ */
+public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
+
+ /**
+ * Internal thread class that is used to determine fingerprints in parallel,
+ * which can take a few seconds.
+ */
+ private class FingerprintThread extends Thread {
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // create file reference for temporary config file
+ final File tempConfigFile =
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "torrc.tmp");
+
+ // compose a modified config file, including a DirServer option with
+ // false fingerprint; this is necessary, because otherwise Tor
+ // would not accept that this router node might have a private IP
+ // address, but connects to the public directory servers
+ final List<String> copyOfConfig =
+ new ArrayList<String>(configuration);
+ final String fakeDirServerString =
+ "DirServer "
+ + nodeName
+ + " 127.0.0.1:"
+ + dirPort
+ + " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
+ copyOfConfig.add(fakeDirServerString);
+
+ // write config file
+ try {
+ final BufferedWriter bw =
+ new BufferedWriter(new FileWriter(tempConfigFile));
+ for (final String c : copyOfConfig) {
+ bw.write(c + "\n");
+ }
+ bw.close();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not write configuration file!", e);
+ logger.log(Level.WARNING, "Could not start Tor process!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ // start process with option --list-fingerprint
+ final ProcessBuilder processBuilder =
+ new ProcessBuilder(torExecutable.getPath(),
+ "--list-fingerprint", "-f", "torrc.tmp");
+ processBuilder.directory(workingDir);
+ processBuilder.redirectErrorStream(true);
+ Process tmpProcess = null;
+ try {
+ tmpProcess = processBuilder.start();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not start Tor process temporarily with "
+ + "--list-fingerprint option!", e);
+ logger.log(Level.WARNING, "Could not start Tor process!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ // wait for process to terminate
+ int exitValue = 0;
+ try {
+ exitValue = tmpProcess.waitFor();
+ } catch (final InterruptedException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Interrupted while waiting for Tor process to exit!",
+ e);
+ logger.log(Level.WARNING,
+ "Temporary Tor process was interrupted!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ if (exitValue != 0) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not start Tor process temporarily with "
+ + "--list-fingerprint option! Tor exited with "
+ + "exit value "
+ + exitValue
+ + "! Please go check the config options in "
+ + tempConfigFile + " manually!");
+ logger.log(Level.WARNING, "Could not start Tor process!", ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ // read fingerprint from file
+ final File fingerprintFile =
+ new File(workingDir.getAbsolutePath() + File.separator
+ + "fingerprint");
+ try {
+ final BufferedReader br2 =
+ new BufferedReader(new FileReader(fingerprintFile));
+ setFingerprint(br2.readLine());
+ br2.close();
+ } catch (final IOException e) {
+ final PuppeTorException ex =
+ new PuppeTorException(
+ "Could not read fingerprint from file!", e);
+ logger.log(Level.WARNING, "Could not read fingerprint file!",
+ ex);
+ setCaughtException(ex);
+ return;
+ }
+
+ // delete temporary config file
+ tempConfigFile.delete();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * Invoked by the fingerprint thread: sets the determined fingerprint string
+ *
+ * @param fingerprint
+ * The determined fingerprint string.
+ */
+ private synchronized void setFingerprint(final String fingerprint) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "setFingerprint",
+ fingerprint);
+
+ // remember fingerprint and notify all waiting threads
+ this.fingerprint = fingerprint;
+ notifyAll();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "setFingerprint");
+ }
+
+ /**
+ * Invoked by the fingerprint thread: sets the exception that occurred when
+ * trying to determine the fingerprint.
+ *
+ * @param caughtException
+ * The exception that occurred when trying to determine the
+ * fingerprint.
+ */
+ protected synchronized void setCaughtException(
+ final PuppeTorException caughtException) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "setCaughtException",
+ caughtException);
+
+ // remember caught exception and notify all waiting threads
+ this.caughtException = caughtException;
+ notifyAll();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "setCaughtException");
+ }
+
+ /**
+ * Port on which the Tor node will be listening for directory requests from
+ * other Tor nodes.
+ */
+ protected int dirPort;
+
+ /**
+ * The IP v4 address on which the node will listen in dotted decimal
+ * notation.
+ */
+ protected String serverIpAddress;
+
+ /**
+ * The fingerprint of this node that is determined as hash value of its
+ * onion key. It is initialized with <code>null</code> and set by the
+ * fingerprint thread as soon as it is determined.
+ */
+ private String fingerprint;
+
+ /**
+ * The exception that was caught when determining the fingerprint of this
+ * node, if any.
+ */
+ protected PuppeTorException caughtException;
+
+ /**
+ * The pattern for valid IP v4 addresses in dotted decimal notation.
+ */
+ private static final Pattern validIpAddressPattern =
+ Pattern
+ .compile("([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
+ + "(\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){1,3}");
+
+ /**
+ * Port on which the Tor node will be listening for onion requests by other
+ * Tor nodes.
+ */
+ protected int orPort;
+
+ /**
+ * Creates a new <code>RouterNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The name of the new node which may only consist of between 1
+ * and 19 alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
+ * @param orPort
+ * Port on which the Tor node will be listening for onion
+ * requests by other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param dirPort
+ * Port on which the Tor node will be listening for directory
+ * requests from other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param serverIpAddress
+ * The IP address on which the node will listen. Must be a valid
+ * IP v4 address in dotted decimal notation. May not be
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ RouterNodeImpl(final NetworkImpl network, final String nodeName,
+ final int controlPort, final int socksPort, final int orPort,
+ final int dirPort, final String serverIpAddress) {
+
+ // create superclass instance; parameter checking is done in super
+ // constructor
+ super(network, nodeName, controlPort, socksPort);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "RouterNodeImpl",
+ new Object[] { network, nodeName, controlPort, socksPort,
+ orPort, dirPort, serverIpAddress });
+
+ // check parameters
+ if (orPort < 0 || orPort > 65535 || dirPort < 0 || dirPort > 65535
+ || serverIpAddress == null
+ || !validIpAddressPattern.matcher(serverIpAddress).matches()) {
+ final IllegalArgumentException e =
+ new IllegalArgumentException("nodeName=" + nodeName
+ + ", controlPort=" + controlPort + ", socksPort="
+ + socksPort + ", orPort=" + orPort + ", dirPort="
+ + dirPort + ", serverIpAddress='" + serverIpAddress
+ + "'");
+ logger.throwing(this.getClass().getName(), "RouterNodeImpl", e);
+ throw e;
+ }
+
+ // remember parameters
+ this.orPort = orPort;
+ this.dirPort = dirPort;
+ this.serverIpAddress = serverIpAddress;
+
+ // extend configuration by template configuration of router nodes
+ configuration.addAll(templateConfiguration);
+
+ // add further configuration to make this node a router node
+ configuration.add("ORPort " + orPort);
+ configuration.add("Nickname " + nodeName);
+
+ // all routers mirror the directory
+ configuration.add("DirPort " + dirPort);
+
+ // the address of this node should be manually specified and not guessed
+ // by Tor
+ configuration.add("Address " + serverIpAddress);
+
+ // the OR port may only be contacted locally
+ configuration.add("ORListenAddress " + serverIpAddress);
+
+ // offer directory only locally (either by being an authority, or by
+ // mirroring it)
+ configuration.add("DirListenAddress " + serverIpAddress);
+
+ // start a thread to determine the node's fingerprint in the background
+ determineFingerprint();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "RouterNodeImpl");
+ }
+
+ public synchronized String getFingerprint() throws PuppeTorException {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "getFingerprint");
+
+ // wait until either the fingerprint has been determined or an exception
+ // was caught
+ while (fingerprint == null && caughtException == null) {
+ try {
+ wait();
+ } catch (final InterruptedException e) {
+ // do nothing
+ }
+ }
+
+ if (caughtException != null) {
+ logger.throwing(this.getClass().getName(), "getFingerprint",
+ caughtException);
+ throw caughtException;
+ }
+
+ // log exiting
+ logger
+ .exiting(this.getClass().getName(), "getFingerprint",
+ fingerprint);
+ return fingerprint;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ", orPort=" + orPort + ", dirPort=" + dirPort;
+ }
+
+ public int getDirPort() {
+ return dirPort;
+ }
+
+ public int getOrPort() {
+ return orPort;
+ }
+
+ /**
+ * Determines the fingerprint of this node by starting a background thread
+ * that performs this operation.
+ */
+ protected synchronized void determineFingerprint() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "determineFingerprint");
+
+ // start a thread to determine this node's fingerprint
+ final FingerprintThread fingerprintThread = new FingerprintThread();
+ fingerprintThread.setName(nodeName + " Fingerprint Resolver");
+ fingerprintThread.start();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "determineFingerprint");
+ }
+
+ /**
+ * Template configuration of router nodes.
+ */
+ static List<String> templateConfiguration;
+
+ static {
+ templateConfiguration = new ArrayList<String>();
+
+ templateConfiguration.add("ContactInfo wont@xxxxxxxxx");
+ templateConfiguration.add("HidServDirectoryV2 1");
+
+ // TODO This is now contained in proposal 135.
+ // templateConfiguration.add("ExitPolicyRejectPrivate 0");
+ // templateConfiguration.add("AssumeReachable 1");
+ // templateConfiguration.add("ServerDNSAllowBrokenResolvConf 1");
+ }
+}
diff --git a/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java b/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
old mode 100755
new mode 100644
index 86dbcfb..ee0e2e6
--- a/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
+++ b/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
@@ -1,377 +1,377 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor.impl;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.uniba.wiai.lspi.puppetor.ServerApplication;
-import de.uniba.wiai.lspi.puppetor.ServerEventType;
-
-/**
- * Implementation of <code>ServerApplication</code>.
- *
- * @author kloesing
- */
-public class ServerApplicationImpl implements ServerApplication {
-
- /**
- * Internal thread class that is used to process an incoming request.
- */
- private class HandlerThread extends Thread {
-
- /**
- * Accepted socket on which the request came in.
- */
- private Socket handleSocket = null;
-
- /**
- * Creates a new thread to handle the request coming in on
- * <code>handleSocket</code>, but does not start handling it.
- *
- * @param handleSocket
- * Accepted socket on which the request came in.
- */
- public HandlerThread(final Socket handleSocket) {
-
- // log entering
- logger.entering(this.getClass().getName(), "HandlerThread",
- handleSocket);
-
- // remember parameter
- this.handleSocket = handleSocket;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "HandlerThread");
- }
-
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- try {
-
- // wait for request (don't mind the content)
- final BufferedReader in =
- new BufferedReader(new InputStreamReader(handleSocket
- .getInputStream()));
- in.read();
-
- // send event to event manager
- eventManager.observeInternalEvent(System.currentTimeMillis(),
- getServerApplicationName(),
- ServerEventType.SERVER_RECEIVING_REQUEST_SENDING_REPLY,
- "Receiving request.");
-
- // write response
- final PrintStream out =
- new PrintStream(handleSocket.getOutputStream());
- out.print("HTTP/1.0 200 OK\r\n");
-
- } catch (final IOException e) {
- logger.log(Level.SEVERE,
- "I/O exception while handling incoming request!");
- // we can't do more, because nobody takes notice of this thread.
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- return;
- // TODO do we need more?
- }
-
- // close socket
- try {
- handleSocket.close();
- } catch (final IOException e) {
- logger
- .log(Level.WARNING,
- "I/O exception while closing socket!");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- return;
- }
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
- }
-
- /**
- * Internal thread class that is used to listen for requests.
- */
- private class ListenThread extends Thread {
-
- /**
- * Flag to remember whether this thread listens for requests at the
- * moment (<code>true</code>), or has been stopped (<code>false</code>).
- */
- private boolean connected;
-
- /**
- * Creates a new thread to listen for requests, but does not start
- * listening, yet.
- */
- ListenThread() {
-
- // log entering
- logger.entering(this.getClass().getName(), "ListenThread");
-
- // start connected
- connected = true;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "ListenThread");
- }
-
- @Override
- public void run() {
-
- // log entering
- logger.entering(this.getClass().getName(), "run");
-
- try {
-
- // create server socket
- ServerSocket serverSocket = null;
- try {
- serverSocket = new ServerSocket(serverPort);
- } catch (final IOException ioe) {
- logger.log(Level.SEVERE,
- "Can't open server socket on port " + serverPort
- + "!");
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- return;
- }
-
- // as long as we are connected, accept incoming requests
- logger.log(Level.FINE, "Listening on port " + serverPort
- + "...");
- while (connected) {
- Socket incomingConnection = null;
- try {
- incomingConnection = serverSocket.accept();
- } catch (final Exception e) {
- logger
- .log(
- Level.SEVERE,
- "Exception while accepting socket requests! Stopping listening!",
- e);
- break;
- }
- new HandlerThread(incomingConnection).start();
- }
-
- } catch (final Exception e) {
-
- // log that we have been interrupted
- logger.log(Level.WARNING, "Server has been interrupted!", e);
- }
-
- // mark as disconnected
- connected = false;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "run");
- }
-
- /**
- * Stops listening on server socket.
- */
- public void stopListening() {
-
- // log entering
- logger.entering(this.getClass().getName(), "stopListening");
-
- // change connected state to false and interrupt thread
- connected = false;
- interrupt();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "stopListening");
- }
- }
-
- /**
- * Event manager that handles all events concerning this server application.
- */
- private final EventManagerImpl eventManager;
-
- /**
- * Logger for this server which is called "server." plus the name of this
- * server application.
- */
- private final Logger logger;
-
- /**
- * Name of this server application that is used as logger name of this node.
- */
- private final String serverApplicationName;
-
- /**
- * Port on which this server will listen for incoming requests.
- */
- private final int serverPort;
-
- /**
- * Thread that listens for requests in the background.
- */
- private Thread serverThread;
-
- /**
- * Creates a new HTTP server application within this JVM, but does not yet
- * listen for incoming requests.
- *
- * @param network
- * Network to which this HTTP server belongs; at the moment this
- * is only used to determine the event manager instance.
- * @param serverApplicationName
- * Name of this server that is used as part of the logger name.
- * @param serverPort
- * Port on which this server will listen for incoming requests.
- * @throws IllegalArgumentException
- * If at least one of the parameters is <code>null</code> or
- * has an invalid value.
- */
- ServerApplicationImpl(final NetworkImpl network,
- final String serverApplicationName, final int serverPort) {
-
- // check if serverApplicationName can be used as logger name
- if (serverApplicationName == null
- || serverApplicationName.length() == 0) {
- throw new IllegalArgumentException(
- "Invalid serverApplicationName: " + serverApplicationName);
- }
-
- // create logger
- logger = Logger.getLogger("server." + serverApplicationName);
-
- // log entering
- logger.entering(this.getClass().getName(), "ServerApplicationImpl",
- new Object[] { network, serverApplicationName, serverPort });
-
- // check parameters
- if (network == null || serverPort < 0 || serverPort > 65535) {
- final IllegalArgumentException e = new IllegalArgumentException();
- logger.throwing(this.getClass().getName(), "ServerApplicationImpl",
- e);
- throw e;
- }
-
- // remember parameters
- this.serverApplicationName = serverApplicationName;
- this.serverPort = serverPort;
-
- // obtain reference on event manager
- eventManager = network.getEventManagerImpl();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "ServerApplicationImpl");
- }
-
- public synchronized void startListening() {
-
- // log entering
- logger.entering(this.getClass().getName(), "listen");
-
- // check if we are already listening
- if (serverThread != null) {
- final IllegalStateException e =
- new IllegalStateException("We are already listening!");
- logger.throwing(this.getClass().getName(), "listen", e);
- throw e;
- }
-
- // create a thread that listens in the background
- serverThread = new ListenThread();
- serverThread.setName("Reply Thread");
- serverThread.setDaemon(true);
- serverThread.start();
-
- // log exiting
- logger.exiting(this.getClass().getName(), "listen");
- }
-
- public synchronized void stopListening() {
-
- // log entering
- logger.entering(this.getClass().getName(), "stopListening");
-
- // check if we are listening
- if (serverThread == null) {
- final IllegalStateException e =
- new IllegalStateException("We are not listening!");
- logger.throwing(this.getClass().getName(), "stopListening", e);
- throw e;
- }
-
- // log this event
- logger.log(Level.FINE, "Shutting down server");
-
- // interrupt thread
- serverThread.interrupt();
-
- // unset listening thread
- serverThread = null;
-
- // log exiting
- logger.exiting(this.getClass().getName(), "stopListening");
- }
-
- public synchronized boolean isListening() {
- return serverThread != null;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": serverApplicationName=\""
- + serverApplicationName + "\", serverPort=" + serverPort;
- }
-
- public String getServerApplicationName() {
- return serverApplicationName;
- }
-
- public int getServerPort() {
- return serverPort;
- }
-}
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.ServerEventType;
+
+/**
+ * Implementation of <code>ServerApplication</code>.
+ *
+ * @author kloesing
+ */
+public class ServerApplicationImpl implements ServerApplication {
+
+ /**
+ * Internal thread class that is used to process an incoming request.
+ */
+ private class HandlerThread extends Thread {
+
+ /**
+ * Accepted socket on which the request came in.
+ */
+ private Socket handleSocket = null;
+
+ /**
+ * Creates a new thread to handle the request coming in on
+ * <code>handleSocket</code>, but does not start handling it.
+ *
+ * @param handleSocket
+ * Accepted socket on which the request came in.
+ */
+ public HandlerThread(final Socket handleSocket) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "HandlerThread",
+ handleSocket);
+
+ // remember parameter
+ this.handleSocket = handleSocket;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "HandlerThread");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+
+ // wait for request (don't mind the content)
+ final BufferedReader in =
+ new BufferedReader(new InputStreamReader(handleSocket
+ .getInputStream()));
+ in.read();
+
+ // send event to event manager
+ eventManager.observeInternalEvent(System.currentTimeMillis(),
+ getServerApplicationName(),
+ ServerEventType.SERVER_RECEIVING_REQUEST_SENDING_REPLY,
+ "Receiving request.");
+
+ // write response
+ final PrintStream out =
+ new PrintStream(handleSocket.getOutputStream());
+ out.print("HTTP/1.0 200 OK\r\n");
+
+ } catch (final IOException e) {
+ logger.log(Level.SEVERE,
+ "I/O exception while handling incoming request!");
+ // we can't do more, because nobody takes notice of this thread.
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
+ // TODO do we need more?
+ }
+
+ // close socket
+ try {
+ handleSocket.close();
+ } catch (final IOException e) {
+ logger
+ .log(Level.WARNING,
+ "I/O exception while closing socket!");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * Internal thread class that is used to listen for requests.
+ */
+ private class ListenThread extends Thread {
+
+ /**
+ * Flag to remember whether this thread listens for requests at the
+ * moment (<code>true</code>), or has been stopped (<code>false</code>).
+ */
+ private boolean connected;
+
+ /**
+ * Creates a new thread to listen for requests, but does not start
+ * listening, yet.
+ */
+ ListenThread() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "ListenThread");
+
+ // start connected
+ connected = true;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "ListenThread");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+
+ // create server socket
+ ServerSocket serverSocket = null;
+ try {
+ serverSocket = new ServerSocket(serverPort);
+ } catch (final IOException ioe) {
+ logger.log(Level.SEVERE,
+ "Can't open server socket on port " + serverPort
+ + "!");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
+ }
+
+ // as long as we are connected, accept incoming requests
+ logger.log(Level.FINE, "Listening on port " + serverPort
+ + "...");
+ while (connected) {
+ Socket incomingConnection = null;
+ try {
+ incomingConnection = serverSocket.accept();
+ } catch (final Exception e) {
+ logger
+ .log(
+ Level.SEVERE,
+ "Exception while accepting socket requests! Stopping listening!",
+ e);
+ break;
+ }
+ new HandlerThread(incomingConnection).start();
+ }
+
+ } catch (final Exception e) {
+
+ // log that we have been interrupted
+ logger.log(Level.WARNING, "Server has been interrupted!", e);
+ }
+
+ // mark as disconnected
+ connected = false;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+
+ /**
+ * Stops listening on server socket.
+ */
+ public void stopListening() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopListening");
+
+ // change connected state to false and interrupt thread
+ connected = false;
+ interrupt();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopListening");
+ }
+ }
+
+ /**
+ * Event manager that handles all events concerning this server application.
+ */
+ private final EventManagerImpl eventManager;
+
+ /**
+ * Logger for this server which is called "server." plus the name of this
+ * server application.
+ */
+ private final Logger logger;
+
+ /**
+ * Name of this server application that is used as logger name of this node.
+ */
+ private final String serverApplicationName;
+
+ /**
+ * Port on which this server will listen for incoming requests.
+ */
+ private final int serverPort;
+
+ /**
+ * Thread that listens for requests in the background.
+ */
+ private Thread serverThread;
+
+ /**
+ * Creates a new HTTP server application within this JVM, but does not yet
+ * listen for incoming requests.
+ *
+ * @param network
+ * Network to which this HTTP server belongs; at the moment this
+ * is only used to determine the event manager instance.
+ * @param serverApplicationName
+ * Name of this server that is used as part of the logger name.
+ * @param serverPort
+ * Port on which this server will listen for incoming requests.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ ServerApplicationImpl(final NetworkImpl network,
+ final String serverApplicationName, final int serverPort) {
+
+ // check if serverApplicationName can be used as logger name
+ if (serverApplicationName == null
+ || serverApplicationName.length() == 0) {
+ throw new IllegalArgumentException(
+ "Invalid serverApplicationName: " + serverApplicationName);
+ }
+
+ // create logger
+ logger = Logger.getLogger("server." + serverApplicationName);
+
+ // log entering
+ logger.entering(this.getClass().getName(), "ServerApplicationImpl",
+ new Object[] { network, serverApplicationName, serverPort });
+
+ // check parameters
+ if (network == null || serverPort < 0 || serverPort > 65535) {
+ final IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "ServerApplicationImpl",
+ e);
+ throw e;
+ }
+
+ // remember parameters
+ this.serverApplicationName = serverApplicationName;
+ this.serverPort = serverPort;
+
+ // obtain reference on event manager
+ eventManager = network.getEventManagerImpl();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "ServerApplicationImpl");
+ }
+
+ public synchronized void startListening() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "listen");
+
+ // check if we are already listening
+ if (serverThread != null) {
+ final IllegalStateException e =
+ new IllegalStateException("We are already listening!");
+ logger.throwing(this.getClass().getName(), "listen", e);
+ throw e;
+ }
+
+ // create a thread that listens in the background
+ serverThread = new ListenThread();
+ serverThread.setName("Reply Thread");
+ serverThread.setDaemon(true);
+ serverThread.start();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "listen");
+ }
+
+ public synchronized void stopListening() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopListening");
+
+ // check if we are listening
+ if (serverThread == null) {
+ final IllegalStateException e =
+ new IllegalStateException("We are not listening!");
+ logger.throwing(this.getClass().getName(), "stopListening", e);
+ throw e;
+ }
+
+ // log this event
+ logger.log(Level.FINE, "Shutting down server");
+
+ // interrupt thread
+ serverThread.interrupt();
+
+ // unset listening thread
+ serverThread = null;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopListening");
+ }
+
+ public synchronized boolean isListening() {
+ return serverThread != null;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": serverApplicationName=\""
+ + serverApplicationName + "\", serverPort=" + serverPort;
+ }
+
+ public String getServerApplicationName() {
+ return serverApplicationName;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+}
diff --git a/tools/create_keystores.sh b/tools/create_keystores.sh
old mode 100755
new mode 100644
--
1.5.6.5