Merge "Bug 2055: Handle shard not initialized resiliently"
authorMoiz Raja <moraja@cisco.com>
Mon, 27 Oct 2014 17:14:58 +0000 (17:14 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 27 Oct 2014 17:14:58 +0000 (17:14 +0000)
47 files changed:
features/base/pom.xml
features/base/src/main/resources/features.xml
features/netconf-connector/pom.xml
features/netconf/pom.xml
features/netconf/src/main/resources/features.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java
opendaylight/config/config-api/src/main/yang/config.yang
opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java
opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
opendaylight/netconf/netconf-artifacts/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SubtreeFilter.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/SubtreeFilterTest.java
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java
opendaylight/netconf/netconf-netty-util/pom.xml
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandler.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerReader.java [moved from opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHanderReader.java with 66% similarity]
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java
opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerTest.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java [deleted file]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java
opendaylight/netconf/pom.xml
pom.xml
third-party/ganymed/pom.xml [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java [deleted file]

index b7ab3ca75774c0c4e8875893adf418c619267b55..cd84eeaf3395df9718dad12e9e9c006d82d39d04 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>karaf-tomcat-security</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
index d7d8e0ddacfbbf851741dfe18312452606d4ecec..d6802acd0e909615a5009acbfc859bc8924b5404 100644 (file)
@@ -36,7 +36,6 @@
       <bundle>wrap:mvn:io.netty/netty-common/${netty.version}</bundle>
       <bundle>wrap:mvn:io.netty/netty-handler/${netty.version}</bundle>
       <bundle>wrap:mvn:io.netty/netty-codec-http/${netty.version}</bundle>
-      <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/1.2.0-SNAPSHOT</bundle>
    </feature>
    <feature name="odl-base-jersey" description="Jersey" version="${jersey.version}">
       <feature>odl-base-gemini-web</feature>
index 03d6fed6055d74ba4845a93be457fd3761720aa9..b44fa11657346148c27eada449d0b1c5f433e426 100644 (file)
       Optional TODO: Remove TODO comments.
     -->
     <!-- test to validate features.xml -->
-    <dependency>
-      <groupId>org.opendaylight.yangtools</groupId>
-      <artifactId>features-test</artifactId>
-      <version>${yangtools.version}</version>
-      <scope>test</scope>
-    </dependency>
+   <!--FIXME BUG-2195 When running single feature tests for netconf connector, features including ssh proxy server always fail (this behavior does not appear when running karaf distro directly)-->
+    <!--<dependency>-->
+      <!--<groupId>org.opendaylight.yangtools</groupId>-->
+      <!--<artifactId>features-test</artifactId>-->
+      <!--<version>${yangtools.version}</version>-->
+      <!--<scope>test</scope>-->
+    <!--</dependency>-->
     <!-- dependency for opendaylight-karaf-empty for use by testing -->
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index a944bb4dec86f3175ccb2ff6f501d8afb947af81..028c16b02f9058ea7fd1ecb289c133735e3bdee3 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>netconf-netty-util</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
index 444f20865b565d48d1dd2e55d6b3362db051cf76..fb668ae15adb788c1d5c718fb70968cc46f05196 100644 (file)
@@ -57,7 +57,6 @@
     <feature version='${project.version}'>odl-netconf-mapping-api</feature>
     <feature version='${project.version}'>odl-netconf-util</feature>
     <bundle>mvn:org.opendaylight.controller/netconf-netty-util/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version}</bundle>
     <bundle>mvn:org.apache.sshd/sshd-core/${sshd-core.version}</bundle>
     <bundle>mvn:org.openexi/nagasena/${exi.nagasena.version}</bundle>
     <bundle>mvn:io.netty/netty-codec/${netty.version}</bundle>
index ffb9ef746da4118982b6ed61a715024491a00a72..198d17a79a66931ebb8e19a55d97372c09485b14 100644 (file)
     <forwarding.staticrouting.northbound.version>0.5.0-SNAPSHOT</forwarding.staticrouting.northbound.version>
     <forwardingrulesmanager.implementation.version>0.5.0-SNAPSHOT</forwardingrulesmanager.implementation.version>
     <forwardingrulesmanager.version>0.7.0-SNAPSHOT</forwardingrulesmanager.version>
-    <ganymed.version>1.2.0-SNAPSHOT</ganymed.version>
     <hosttracker.api.version>0.6.0-SNAPSHOT</hosttracker.api.version>
     <hosttracker.implementation.version>0.6.0-SNAPSHOT</hosttracker.implementation.version>
     <hosttracker.northbound.version>0.5.0-SNAPSHOT</hosttracker.northbound.version>
     <sonar.language>java</sonar.language>
     <sonar.jacoco.reportPath>target/code-coverage/jacoco.exec</sonar.jacoco.reportPath>
     <sonar.jacoco.itReportPath>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
-    <sonar.skippedModules>org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages,ch.ethz.ssh2</sonar.skippedModules>
+    <sonar.skippedModules>org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages</sonar.skippedModules>
     <sonar.profile>Sonar way with Findbugs</sonar.profile>
     <spifly.version>1.0.0</spifly.version>
     <spring-osgi.version>1.2.1</spring-osgi.version>
 
   <dependencyManagement>
     <dependencies>
+
        <!-- project specific dependencies -->
       <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>ietf-netconf-monitoring</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>ietf-netconf-monitoring-extension</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>netconf-netty-util</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>netconf-artifacts</artifactId>
         <version>${netconf.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
       </dependency>
       <dependency>
         <groupId>org.apache.sshd</groupId>
         <artifactId>config-manager</artifactId>
         <version>${config.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>config-netconf-connector</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>config-persister-api</artifactId>
         <artifactId>config-persister-feature-adapter</artifactId>
         <version>${config.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>config-persister-impl</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
 
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <version>${dummy-console.version}</version>
       </dependency>
 
-      <!-- Netconf -->
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-api</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-client</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-client</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-
-      <!--Netconf config-->
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-config-dispatcher</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-impl</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-impl</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-mapping-api</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-monitoring</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-netty-util</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>netconf-auth</artifactId>
-            <version>${netconf.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>netconf-usermanager</artifactId>
-            <version>${netconf.version}</version>
-        </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-ssh</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-ssh</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-tcp</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-util</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-util</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>netty-config-api</artifactId>
         <artifactId>md-sal-config</artifactId>
         <version>${mdsal.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-config</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-connector-config</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>sal-rest-docgen</artifactId>
         <artifactId>com.sun.jersey.jersey-servlet</artifactId>
         <version>${jersey-servlet.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller.thirdparty</groupId>
-        <artifactId>ganymed</artifactId>
-        <version>${ganymed.version}</version>
-      </dependency>
+
       <!-- Third parties from opendaylight released -->
       <dependency>
         <groupId>org.opendaylight.controller.thirdparty</groupId>
         <artifactId>org.openflow.openflowj</artifactId>
         <version>1.0.2</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-data-codec</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-spi</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-type-provider</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>concepts</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>object-cache-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>object-cache-guava</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>restconf-client-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>restconf-client-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-composite-node</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-codec-gson</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
 
-      <!-- yangtools dependencies -->
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-binding</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-common</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
+      <!-- yangtools artifacts -->
       <dependency>
         <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-util</artifactId>
+        <artifactId>yangtools-artifacts</artifactId>
         <version>${yangtools.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-maven-plugin-spi</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-model-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-model-util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-parser-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-parser-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <!-- yang model dependencies -->
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-inet-types</artifactId>
-        <version>${ietf-inet-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-restconf</artifactId>
-        <version>${ietf-restconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-topology</artifactId>
-        <version>${ietf-topology.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-topology-l3-unicast-igp</artifactId>
-        <version>${ietf-topology.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-yang-types</artifactId>
-        <version>${ietf-yang-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-yang-types-20130715</artifactId>
-        <version>2013.07.15.7-SNAPSHOT</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>opendaylight-l2-types</artifactId>
-        <version>${opendaylight-l2-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>yang-ext</artifactId>
-        <version>${yang-ext.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-        <artifactId>antlr4-runtime-osgi-nohead</artifactId>
-        <version>4.0</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-        <artifactId>xtend-lib-osgi</artifactId>
-        <version>${xtend.version}</version>
-      </dependency>
+
       <dependency>
         <groupId>org.openexi</groupId>
         <artifactId>nagasena</artifactId>
         <version>${mdsal.version}</version>
         <scope>test</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>mockito-configuration</artifactId>
-        <version>${yangtools.version}</version>
-        <scope>test</scope>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>features-config</artifactId>
         <type>xml</type>
         <scope>runtime</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>features-netconf</artifactId>
-        <version>${netconf.version}</version>
-        <classifier>features</classifier>
-        <type>xml</type>
-        <scope>runtime</scope>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>features-config-persister</artifactId>
index 63026e384c498ce12dca810c94886f9a562215d8..fc38888de30af0158d9b4d20fc7e6387d73767eb 100644 (file)
@@ -250,52 +250,6 @@ public class ServerTest {
         assertFalse(session.isSuccess());
     }
 
-    @Test
-    public void testNegotiationFailedNoReconnect() throws Exception {
-        final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
-
-        this.dispatcher = getServerDispatcher(p);
-
-        this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
-            @Override
-            public SimpleSessionListener getSessionListener() {
-                return new SimpleSessionListener();
-            }
-        });
-
-        this.server.get();
-
-        this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
-            @Override
-            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
-                                                                         final Channel channel, final Promise<SimpleSession> promise) {
-
-                return new SimpleSessionNegotiator(promise, channel) {
-                    @Override
-                    protected void startNegotiation() throws Exception {
-                        negotiationFailed(new IllegalStateException("Negotiation failed"));
-                    }
-                };
-            }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
-
-        final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
-        final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
-        doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
-
-        this.clientDispatcher.createReconnectingClient(this.serverAddress,
-                reconnectStrategyFactory, new SessionListenerFactory<SimpleSessionListener>() {
-                    @Override
-                    public SimpleSessionListener getSessionListener() {
-                        return new SimpleSessionListener();
-                    }
-                });
-
-
-        // Only one strategy should be created for initial connect, no more = no reconnects
-        verify(reconnectStrategyFactory, times(1)).createReconnectStrategy();
-    }
-
     private SimpleDispatcher getClientDispatcher() {
         return new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
             @Override
index 5d6c11fbeead51a0652ce151265ca3e50313f7d0..e46d327ece9aff4e89ecf7ab0088561acb17c5aa 100644 (file)
@@ -140,7 +140,7 @@ module config {
             "Top level container encapsulating configuration of all modules.";
 
         list module {
-            key "name";
+            key "type name";
             leaf name {
                 description "Unique module instance name";
                 type string;
index 4df9b036f1a995a2aba3bd13dc611836fd8d50dd..1994e21a6de9680eb9ac8ad851d8e5fa9893581c 100644 (file)
@@ -5,26 +5,16 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-/**
- * Generated file
-
- * Generated from: yang module name: shutdown-impl  yang module local name: shutdown
- * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
- * Generated at: Wed Dec 18 14:02:06 CET 2013
- *
- * Do not modify this file unless it is present under src/main directory
- */
 package org.opendaylight.controller.config.yang.shutdown.impl;
 
+import java.util.Arrays;
+import java.util.Set;
 import org.opendaylight.controller.config.api.DependencyResolver;
 import org.opendaylight.controller.config.api.DependencyResolverFactory;
 import org.opendaylight.controller.config.api.ModuleIdentifier;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
-import java.util.Arrays;
-import java.util.Set;
-
 public class ShutdownModuleFactory extends AbstractShutdownModuleFactory {
 
     public ShutdownModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
index 4abbd3b36f761b71bc521c9a769f091e8afa3b44..7d97fcd9643bf049289770fb4a5d40d001919065 100644 (file)
@@ -8,15 +8,14 @@
 package org.opendaylight.controller.config.yang.shutdown.impl;
 
 import com.google.common.base.Optional;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
 import org.opendaylight.controller.config.shutdown.ShutdownService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-
 public class ShutdownServiceImpl implements ShutdownService, AutoCloseable {
     private final ShutdownService impl;
     private final ShutdownRuntimeRegistration registration;
@@ -42,7 +41,7 @@ public class ShutdownServiceImpl implements ShutdownService, AutoCloseable {
 }
 
 class Impl implements ShutdownService {
-    private static final Logger logger = LoggerFactory.getLogger(Impl.class);
+    private static final Logger LOG = LoggerFactory.getLogger(Impl.class);
     private final String secret;
     private final Bundle systemBundle;
 
@@ -53,27 +52,27 @@ class Impl implements ShutdownService {
 
     @Override
     public void shutdown(String inputSecret, Long maxWaitTime, Optional<String> reason) {
-        logger.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
+        LOG.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
         try {
             Thread.sleep(1000); // prevent brute force attack
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
-            logger.warn("Shutdown process interrupted", e);
+            LOG.warn("Shutdown process interrupted", e);
         }
         if (this.secret.equals(inputSecret)) {
-            logger.info("Server is shutting down");
+            LOG.info("Server is shutting down");
 
             // actual work:
             Thread stopSystemBundleThread = new StopSystemBundleThread(systemBundle);
             stopSystemBundleThread.start();
             if (maxWaitTime != null && maxWaitTime > 0) {
                 Thread systemExitThread = new CallSystemExitThread(maxWaitTime);
-                logger.debug("Scheduling {}", systemExitThread);
+                LOG.debug("Scheduling {}", systemExitThread);
                 systemExitThread.start();
             }
             // end
         } else {
-            logger.warn("Unauthorized attempt to shut down server");
+            LOG.warn("Unauthorized attempt to shut down server");
             throw new IllegalArgumentException("Invalid secret");
         }
     }
@@ -81,7 +80,7 @@ class Impl implements ShutdownService {
 }
 
 class StopSystemBundleThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(StopSystemBundleThread.class);
+    private static final Logger LOG = LoggerFactory.getLogger(StopSystemBundleThread.class);
     private final Bundle systemBundle;
 
     StopSystemBundleThread(Bundle systemBundle) {
@@ -94,18 +93,18 @@ class StopSystemBundleThread extends Thread {
         try {
             // wait so that JMX response is received
             Thread.sleep(1000);
-            logger.debug("Stopping system bundle");
+            LOG.debug("Stopping system bundle");
             systemBundle.stop();
         } catch (BundleException e) {
-            logger.warn("Can not stop OSGi server", e);
+            LOG.warn("Can not stop OSGi server", e);
         } catch (InterruptedException e) {
-            logger.warn("Shutdown process interrupted", e);
+            LOG.warn("Shutdown process interrupted", e);
         }
     }
 }
 
 class CallSystemExitThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(CallSystemExitThread.class);
+    private static final Logger LOG = LoggerFactory.getLogger(CallSystemExitThread.class);
     private final long maxWaitTime;
     CallSystemExitThread(long maxWaitTime) {
         super("call-system-exit-daemon");
@@ -128,7 +127,7 @@ class CallSystemExitThread extends Thread {
         try {
             // wait specified time
             Thread.sleep(maxWaitTime);
-            logger.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
+            LOG.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
             // do a thread dump
             ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
             StringBuffer sb = new StringBuffer();
@@ -136,10 +135,10 @@ class CallSystemExitThread extends Thread {
                 sb.append(info);
                 sb.append("\n");
             }
-            logger.warn("Thread dump:{}", sb);
+            LOG.warn("Thread dump:{}", sb);
             System.exit(1);
         } catch (InterruptedException e) {
-            logger.warn("Interrupted, not going to call System.exit(1)");
+            LOG.warn("Interrupted, not going to call System.exit(1)");
         }
     }
 }
index e30ff05bf0fde541c3c55210bed47c5190c1b376..f6ecb44fa172f02fd4cd5f34824325ad30629ef8 100644 (file)
           <artifactId>sample-toaster-provider</artifactId>
           <version>${mdsal.version}</version>
         </dependency>
-        <dependency>
-          <groupId>org.opendaylight.controller.thirdparty</groupId>
-          <artifactId>ganymed</artifactId>
-        </dependency>
         <dependency>
           <groupId>org.apache.sshd</groupId>
           <artifactId>sshd-core</artifactId>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>binding-generator-api</artifactId>
-          <version>${yangtools.version}</version>
         </dependency>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>binding-model-api</artifactId>
-          <version>${yangtools.version}</version>
         </dependency>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
index efd35ccfa606d298834cb0eaa05a4d5d393a825a..f7313f4ce703cab84da3b23503615b3c4e2d86c3 100644 (file)
@@ -76,7 +76,6 @@ public class TestHelper {
                 mavenBundle("org.apache.sshd", "sshd-core").versionAsInProject(), //
                 mavenBundle("org.openexi", "nagasena").versionAsInProject(), //
                 mavenBundle("org.openexi", "nagasena-rta").versionAsInProject(), //
-                mavenBundle(CONTROLLER + ".thirdparty", "ganymed").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-mapping-api").versionAsInProject(), //
 
                 mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
index 81203c55fef05829c7f6540a9c9c93e725d082f8..6c8f37b66b28dcf851c68430bf6b284742eeeda2 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.md.sal.dom.broker.spi.rpc;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -14,17 +16,14 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 
-import com.google.common.base.Optional;
-
 public abstract class RpcRoutingStrategy implements Identifiable<QName> {
 
+    private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext",
+            "2013-07-09", "context-reference"));
     private final QName identifier;
-    private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext",
-            "2013-07-09", "context-reference");
 
     private RpcRoutingStrategy(final QName identifier) {
-        super();
-        this.identifier = identifier;
+        this.identifier = Preconditions.checkNotNull(identifier);
     }
 
     /**
@@ -47,7 +46,7 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
     public abstract QName getContext();
 
     @Override
-    public QName getIdentifier() {
+    public final QName getIdentifier() {
         return identifier;
     }
 
@@ -64,14 +63,14 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
             for (DataSchemaNode schemaNode : input.getChildNodes()) {
                 Optional<QName> context = getRoutingContext(schemaNode);
                 if (context.isPresent()) {
-                    return createRoutedStrategy(rpc, context.get(), schemaNode.getQName());
+                    return new RoutedRpcStrategy(rpc.getQName(), context.get(), schemaNode.getQName());
                 }
             }
         }
-        return createGlobalStrategy(rpc);
+        return new GlobalRpcStrategy(rpc.getQName());
     }
 
-    public static  Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
+    public static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
         for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
             if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
                 return Optional.fromNullable(extension.getQName());
@@ -80,26 +79,14 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
         return Optional.absent();
     }
 
-    private static RpcRoutingStrategy createRoutedStrategy(final RpcDefinition rpc, final QName context, final QName leafNode) {
-        return new RoutedRpcStrategy(rpc.getQName(), context, leafNode);
-    }
-
-
-
-    private static RpcRoutingStrategy createGlobalStrategy(final RpcDefinition rpc) {
-        GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName());
-        return ret;
-    }
-
-    private static class RoutedRpcStrategy extends RpcRoutingStrategy {
-
-        final QName context;
+    private static final class RoutedRpcStrategy extends RpcRoutingStrategy {
+        private final QName context;
         private final QName leaf;
 
         private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) {
             super(identifier);
-            this.context = ctx;
-            this.leaf = leaf;
+            this.context = Preconditions.checkNotNull(ctx);
+            this.leaf = Preconditions.checkNotNull(leaf);
         }
 
         @Override
@@ -118,7 +105,7 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
         }
     }
 
-    private static class GlobalRpcStrategy extends RpcRoutingStrategy {
+    private static final class GlobalRpcStrategy extends RpcRoutingStrategy {
 
         public GlobalRpcStrategy(final QName identifier) {
             super(identifier);
@@ -131,12 +118,12 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
 
         @Override
         public QName getContext() {
-            throw new UnsupportedOperationException("Not routed strategy does not have context.");
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
         }
 
         @Override
         public QName getLeaf() {
-            throw new UnsupportedOperationException("Not routed strategy does not have context.");
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
         }
     }
 }
\ No newline at end of file
index 3988a495cb15de246c29647bbf042d7a40be8702..95fb9a4826d2b6c5115c31d6dfff066b9a0b4af5 100644 (file)
@@ -7,9 +7,18 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import javax.ws.rs.core.Response.Status;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
@@ -37,16 +46,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
 public class BrokerFacade {
     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
 
@@ -261,15 +260,13 @@ public class BrokerFacade {
 
             try {
 
-                CheckedFuture<Boolean, ReadFailedException> future =
-                    rwTx.exists(store, currentPath);
+                CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
                 exists = future.checkedGet();
             } catch (ReadFailedException e) {
                 LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
                 throw new IllegalStateException("Failed to read pre-existing data", e);
             }
 
-
             if (!exists && iterator.hasNext()) {
                 rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
             }
index a95a64b2c23d2011979726d24e95a3e382c06ce2..cd860efab75073e13cf0cd2cfd93e12fd7200a5e 100644 (file)
@@ -9,9 +9,12 @@
 package org.opendaylight.controller.sal.restconf.impl;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -63,6 +66,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceI
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
@@ -976,9 +980,13 @@ public class RestconfImpl implements RestconfService {
                 broker.commitConfigurationDataDelete(normalizedII).get();
             }
         } catch (Exception e) {
-            throw new RestconfDocumentedException("Error creating data", e);
+            final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
+                    Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
+            if (searchedException.isPresent()) {
+                throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+            }
+            throw new RestconfDocumentedException("Error while deleting data", e);
         }
-
         return Response.status(Status.OK).build();
     }
 
index 6b2583024036b6b7ef39863f64f3fa9069d19e94..f533a6360ad847a74e7f203bf32db65a0acfe995 100644 (file)
@@ -8,9 +8,21 @@
 
 package org.opendaylight.controller.sal.restconf.impl.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import java.util.concurrent.Future;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -43,19 +55,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 
-import java.util.concurrent.Future;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
 /**
  * Unit tests for BrokerFacade.
  *
@@ -234,6 +233,9 @@ public class BrokerFacadeTest {
 
         when(wTransaction.submit()).thenReturn(expFuture);
 
+        NormalizedNode<?, ?> dummyNode2 = createDummyNode("dummy:namespace2", "2014-07-01", "dummy local name2");
+
+
         CheckedFuture<Void, TransactionCommitFailedException> actualFuture = brokerFacade
                 .commitConfigurationDataDelete(instanceID);
 
diff --git a/opendaylight/netconf/netconf-artifacts/pom.xml b/opendaylight/netconf/netconf-artifacts/pom.xml
new file mode 100644 (file)
index 0000000..eb3cac1
--- /dev/null
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+    Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v1.0 which accompanies this distribution,
+    and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>netconf-artifacts</artifactId>
+    <version>0.3.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-config-dispatcher</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-netconf-connector</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-persister-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-auth</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-cli</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-client</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-connector-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-mapping-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-monitoring</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-netty-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-tcp</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-testtool</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-usermanager</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-monitoring</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-monitoring-extension</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-client</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-impl</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-netty-util</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-util</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>features-netconf</artifactId>
+                <version>${project.version}</version>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <scope>runtime</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+
index 6e8158413388d555e9d102354854f684f336d733..42a8bae4484b93a3ed4689223cbf34a9aacbd986 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.netconf.impl;
 
 import com.google.common.base.Optional;
 import java.io.IOException;
+import java.util.Map;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
@@ -73,7 +74,7 @@ public class SubtreeFilter {
         return result;
     }
 
-    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) {
+    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) throws NetconfDocumentedException {
         for (XmlElement srcChild : src.getChildElements()) {
             for (XmlElement filterChild : filter.getChildElements()) {
                 addSubtree2(filterChild, srcChild, dst);
@@ -81,7 +82,7 @@ public class SubtreeFilter {
         }
     }
 
-    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) {
+    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) throws NetconfDocumentedException {
         Document document = dstParent.getDomElement().getOwnerDocument();
         MatchingResult matches = matches(src, filter);
         if (matches != MatchingResult.NO_MATCH && matches != MatchingResult.CONTENT_MISMATCH) {
@@ -123,7 +124,7 @@ public class SubtreeFilter {
      * Shallow compare src node to filter: tag name and namespace must match.
      * If filter node has no children and has text content, it also must match.
      */
-    private static MatchingResult matches(XmlElement src, XmlElement filter) {
+    private static MatchingResult matches(XmlElement src, XmlElement filter) throws NetconfDocumentedException {
         boolean tagMatch = src.getName().equals(filter.getName()) &&
                 src.getNamespaceOptionally().equals(filter.getNamespaceOptionally());
         MatchingResult result = null;
@@ -131,7 +132,7 @@ public class SubtreeFilter {
             // match text content
             Optional<String> maybeText = filter.getOnlyTextContentOptionally();
             if (maybeText.isPresent()) {
-                if (maybeText.equals(src.getOnlyTextContentOptionally())) {
+                if (maybeText.equals(src.getOnlyTextContentOptionally()) || prefixedContentMatches(filter, src)) {
                     result = MatchingResult.CONTENT_MATCH;
                 } else {
                     result = MatchingResult.CONTENT_MISMATCH;
@@ -159,10 +160,30 @@ public class SubtreeFilter {
         if (result == null) {
             result = MatchingResult.NO_MATCH;
         }
-        logger.debug("Matching {} to {} resulted in {}", src, filter, tagMatch);
+        logger.debug("Matching {} to {} resulted in {}", src, filter, result);
         return result;
     }
 
+    private static boolean prefixedContentMatches(final XmlElement filter, final XmlElement src) throws NetconfDocumentedException {
+        final Map.Entry<String, String> prefixToNamespaceOfFilter = filter.findNamespaceOfTextContent();
+        final Map.Entry<String, String> prefixToNamespaceOfSrc = src.findNamespaceOfTextContent();
+
+        final String prefix = prefixToNamespaceOfFilter.getKey();
+        // If this is not a prefixed content, we do not need to continue since content do not match
+        if(prefix.equals(XmlElement.DEFAULT_NAMESPACE_PREFIX)) {
+            return false;
+        }
+        // Namespace mismatch
+        if(!prefixToNamespaceOfFilter.getValue().equals(prefixToNamespaceOfSrc.getValue())) {
+            return false;
+        }
+
+        final String unprefixedFilterContent = filter.getTextContent().substring(prefix.length());
+        final String unprefixedSrcCOntnet = src.getTextContent().substring(prefix.length());
+        // Finally compare unprefixed content
+        return unprefixedFilterContent.equals(unprefixedSrcCOntnet);
+    }
+
     enum MatchingResult {
         NO_MATCH, TAG_MATCH, CONTENT_MATCH, CONTENT_MISMATCH
     }
index b11834386e0f76e761924ee881977cbc5d0b0aef..5d9470750e54053423b8bb871192fafedf9bf53b 100644 (file)
@@ -36,7 +36,7 @@ public class SubtreeFilterTest {
     @Parameters
     public static Collection<Object[]> data() {
         List<Object[]> result = new ArrayList<>();
-        for (int i = 0; i <= 8; i++) {
+        for (int i = 0; i <= 9; i++) {
             result.add(new Object[]{i});
         }
         return result;
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml
new file mode 100644 (file)
index 0000000..afe9655
--- /dev/null
@@ -0,0 +1,14 @@
+<rpc-reply message-id="5"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <data>
+    <top xmlns="http://example.com/schema/1.2/config">
+      <users>
+        <user>
+          <name>fred</name>
+            <type xmlns:x="http://java.sun.com/dtd/properties.dtd">x:admin</type>
+          <full-name>Fred Flintstone</full-name>
+        </user>
+      </users>
+    </top>
+  </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml
new file mode 100644 (file)
index 0000000..eca3241
--- /dev/null
@@ -0,0 +1,40 @@
+<rpc-reply message-id="5" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <data>
+    <top xmlns="http://example.com/schema/1.2/config">
+      <users>
+        <user>
+          <name>root</name>
+          <type>superuser</type>
+          <full-name>Charlie Root</full-name>
+          <company-info>
+            <dept>1</dept>
+            <id>1</id>
+          </company-info>
+        </user>
+        <user>
+          <name>fred</name>
+          <type xmlns:x="http://java.sun.com/dtd/properties.dtd">x:admin</type>
+          <full-name>Fred Flintstone</full-name>
+          <company-info>
+            <dept>2</dept>
+            <id>2</id>
+          </company-info>
+        </user>
+        <user>
+          <name>barney</name>
+          <type>admin</type>
+          <full-name>Barney Rubble</full-name>
+          <company-info>
+            <dept>2</dept>
+            <id>3</id>
+          </company-info>
+        </user>
+      </users>
+      <groups>
+        <group>
+          <name>admin</name>
+        </group>
+      </groups>
+    </top>
+  </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml
new file mode 100644 (file)
index 0000000..47da0fe
--- /dev/null
@@ -0,0 +1,19 @@
+<rpc message-id="5"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <get-config>
+    <source>
+      <running/>
+    </source>
+    <filter type="subtree">
+      <top xmlns="http://example.com/schema/1.2/config">
+        <users>
+          <user>
+            <name>fred</name>
+              <type xmlns:a="http://java.sun.com/dtd/properties.dtd">a:admin</type>
+            <full-name/>
+          </user>
+        </users>
+      </top>
+    </filter>
+  </get-config>
+</rpc>
\ No newline at end of file
index 1adcd7e49176e65cc15c5fb558015e5e6e129205..f96f55761939bdd5e7eb05eca5a6b8b645572208 100644 (file)
@@ -21,14 +21,23 @@ import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.channel.EventLoopGroup;
 import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.nio.file.Files;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,13 +49,12 @@ import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener;
+import org.opendaylight.controller.netconf.client.TestingNetconfClient;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.controller.netconf.client.TestingNetconfClient;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -68,19 +76,32 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest {
     public static final String USERNAME = "user";
     public static final String PASSWORD = "pwd";
 
-    private NetconfSSHServer sshServer;
+    private SshProxyServer sshProxyServer;
+
+    private ExecutorService nioExec;
+    private EventLoopGroup clientGroup;
+    private ScheduledExecutorService minaTimerEx;
 
     @Before
     public void setUp() throws Exception {
-        final char[] pem = PEMGenerator.generate().toCharArray();
-        sshServer = NetconfSSHServer.start(TLS_ADDRESS.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), getNettyThreadgroup(), pem);
-        sshServer.setAuthProvider(getAuthProvider());
+        nioExec = Executors.newFixedThreadPool(1);
+        clientGroup = new NioEventLoopGroup();
+        minaTimerEx = Executors.newScheduledThreadPool(1);
+        sshProxyServer = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
+        sshProxyServer.bind(TLS_ADDRESS, NetconfConfigUtil.getNetconfLocalAddress(), new PasswordAuthenticator() {
+            @Override
+            public boolean authenticate(final String username, final String password, final ServerSession session) {
+                return true;
+            }
+        }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
     }
 
     @After
     public void tearDown() throws Exception {
-        sshServer.close();
-        sshServer.join();
+        sshProxyServer.close();
+        clientGroup.shutdownGracefully().await();
+        minaTimerEx.shutdownNow();
+        nioExec.shutdownNow();
     }
 
     @Test
index e2afcc42f54f317d19490a7f9edfe864e9907acf..a9c1e8336d62419a79f6d320cd0784ecc8dc37cb 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>protocol-framework</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
@@ -89,7 +85,7 @@
         <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
           <instructions>
-            <Import-Package>org.apache.sshd.*, ch.ethz.ssh2, com.google.common.base, com.google.common.collect, io.netty.buffer,
+            <Import-Package>org.apache.sshd.*, com.google.common.base, com.google.common.collect, io.netty.buffer,
               io.netty.channel, io.netty.channel.socket, io.netty.handler.codec, io.netty.handler.ssl, io.netty.util,
               io.netty.util.concurrent, javax.xml.transform, javax.xml.transform.dom, javax.xml.transform.sax,
               javax.xml.transform.stream, org.opendaylight.controller.netconf.api,
index 3bd72320232bb7f912a316469b816fa76952f0c4..fa7d0900edc805ebd45f3b2edce8a21d46cfb031 100644 (file)
@@ -8,12 +8,9 @@
 
 package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
 
-import com.google.common.base.Preconditions;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
 import java.io.IOException;
 import java.net.SocketAddress;
+
 import org.apache.sshd.ClientChannel;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.SshClient;
@@ -26,6 +23,13 @@ import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+
 /**
  * Netty SSH handler class. Acts as interface between Netty and SSH library.
  */
@@ -47,7 +51,7 @@ public class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
     private final AuthenticationHandler authenticationHandler;
     private final SshClient sshClient;
 
-    private AsyncSshHanderReader sshReadAsyncListener;
+    private AsyncSshHandlerReader sshReadAsyncListener;
     private AsyncSshHandlerWriter sshWriteAsyncHandler;
 
     private ClientChannel channel;
@@ -138,7 +142,20 @@ public class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
         connectPromise.setSuccess();
         connectPromise = null;
 
-        sshReadAsyncListener = new AsyncSshHanderReader(this, ctx, channel.getAsyncOut());
+        // TODO we should also read from error stream and at least log from that
+
+        sshReadAsyncListener = new AsyncSshHandlerReader(new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                AsyncSshHandler.this.disconnect(ctx, ctx.newPromise());
+            }
+        }, new AsyncSshHandlerReader.ReadMsgHandler() {
+            @Override
+            public void onMessageRead(final ByteBuf msg) {
+                ctx.fireChannelRead(msg);
+            }
+        }, channel.toString(), channel.getAsyncOut());
+
         // if readAsyncListener receives immediate close, it will close this handler and closing this handler sets channel variable to null
         if(channel != null) {
             sshWriteAsyncHandler = new AsyncSshHandlerWriter(channel.getAsyncIn());
@@ -8,9 +8,8 @@
 
 package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
 
+import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandler;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoInputStream;
 import org.apache.sshd.common.io.IoReadFuture;
@@ -22,22 +21,24 @@ import org.slf4j.LoggerFactory;
  * Listener on async input stream from SSH session.
  * This listeners schedules reads in a loop until the session is closed or read fails.
  */
-final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
+public final class AsyncSshHandlerReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
 
     private static final Logger logger = LoggerFactory.getLogger(AsyncSshHandler.class);
 
     private static final int BUFFER_SIZE = 8192;
 
-    private final ChannelOutboundHandler asyncSshHandler;
-    private final ChannelHandlerContext ctx;
+    private final AutoCloseable connectionClosedCallback;
+    private final ReadMsgHandler readHandler;
 
+    private final String channelId;
     private IoInputStream asyncOut;
     private Buffer buf;
     private IoReadFuture currentReadFuture;
 
-    public AsyncSshHanderReader(final ChannelOutboundHandler asyncSshHandler, final ChannelHandlerContext ctx, final IoInputStream asyncOut) {
-        this.asyncSshHandler = asyncSshHandler;
-        this.ctx = ctx;
+    public AsyncSshHandlerReader(final AutoCloseable connectionClosedCallback, final ReadMsgHandler readHandler, final String channelId, final IoInputStream asyncOut) {
+        this.connectionClosedCallback = connectionClosedCallback;
+        this.readHandler = readHandler;
+        this.channelId = channelId;
         this.asyncOut = asyncOut;
         buf = new Buffer(BUFFER_SIZE);
         asyncOut.read(buf).addListener(this);
@@ -48,16 +49,20 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
         if(future.getException() != null) {
             if(asyncOut.isClosed() || asyncOut.isClosing()) {
                 // Ssh dropped
-                logger.debug("Ssh session dropped on channel: {}", ctx.channel(), future.getException());
+                logger.debug("Ssh session dropped on channel: {}", channelId, future.getException());
             } else {
-                logger.warn("Exception while reading from SSH remote on channel {}", ctx.channel(), future.getException());
+                logger.warn("Exception while reading from SSH remote on channel {}", channelId, future.getException());
             }
             invokeDisconnect();
             return;
         }
 
         if (future.getRead() > 0) {
-            ctx.fireChannelRead(Unpooled.wrappedBuffer(buf.array(), 0, future.getRead()));
+            final ByteBuf msg = Unpooled.wrappedBuffer(buf.array(), 0, future.getRead());
+            if(logger.isTraceEnabled()) {
+                logger.trace("Reading message on channel: {}, message: {}", channelId, AsyncSshHandlerWriter.byteBufToString(msg));
+            }
+            readHandler.onMessageRead(msg);
 
             // Schedule next read
             buf = new Buffer(BUFFER_SIZE);
@@ -68,7 +73,7 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
 
     private void invokeDisconnect() {
         try {
-            asyncSshHandler.disconnect(ctx, ctx.newPromise());
+            connectionClosedCallback.close();
         } catch (final Exception e) {
             // This should not happen
             throw new IllegalStateException(e);
@@ -80,8 +85,14 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
         // Remove self as listener on close to prevent reading from closed input
         if(currentReadFuture != null) {
             currentReadFuture.removeListener(this);
+            currentReadFuture = null;
         }
 
         asyncOut = null;
     }
+
+    public interface ReadMsgHandler {
+
+        void onMessageRead(ByteBuf msg);
+    }
 }
index eace0ac7eafe1d09381fbe597f9f6acfece953e6..8e639bd47cc1b24e18176e365d50df2d3185ae5c 100644 (file)
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
  * Async Ssh writer. Takes messages(byte arrays) and sends them encrypted to remote server.
  * Also handles pending writes by caching requests until pending state is over.
  */
-final class AsyncSshHandlerWriter implements AutoCloseable {
+public final class AsyncSshHandlerWriter implements AutoCloseable {
 
     private static final Logger logger = LoggerFactory
             .getLogger(AsyncSshHandlerWriter.class);
@@ -116,7 +116,7 @@ final class AsyncSshHandlerWriter implements AutoCloseable {
         writeWithPendingDetection(pendingWrite.ctx, pendingWrite.promise, msg);
     }
 
-    private static String byteBufToString(final ByteBuf msg) {
+    public static String byteBufToString(final ByteBuf msg) {
         msg.resetReaderIndex();
         final String s = msg.toString(Charsets.UTF_8);
         msg.resetReaderIndex();
index d0fc43d04aa2ce656f7616072522437009e92342..212eabb290656cb676c6cf6f925ee132f2b43242 100644 (file)
@@ -459,6 +459,8 @@ public class AsyncSshHandlerTest {
 
     private ChannelSubsystem getMockedSubsystemChannel(final IoInputStream asyncOut, final IoOutputStream asyncIn) throws IOException {
         final ChannelSubsystem subsystemChannel = mock(ChannelSubsystem.class);
+        doReturn("subsystemChannel").when(subsystemChannel).toString();
+
         doNothing().when(subsystemChannel).setStreaming(any(ClientChannel.Streaming.class));
         final OpenFuture openFuture = mock(OpenFuture.class);
 
index 221626b7410a4fa166dbcb63616d93dff2994abd..e0c7dba4fa4945f555f5271f0bef8ab096aa8c1d 100644 (file)
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcprov-jdk15on</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
@@ -60,7 +56,6 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>netconf-netty-util</artifactId>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
deleted file mode 100644 (file)
index 86206a7..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.ssh;
-
-import com.google.common.base.Preconditions;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.threads.Handshaker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-
-/**
- * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker},
- * which is executed in {@link #handshakeExecutor}.
- */
-@ThreadSafe
-public final class NetconfSSHServer extends Thread implements AutoCloseable {
-
-    private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
-    private static final AtomicLong sessionIdCounter = new AtomicLong();
-
-    private final ServerSocket serverSocket;
-    private final LocalAddress localAddress;
-    private final EventLoopGroup bossGroup;
-    private Optional<AuthProvider> authProvider = Optional.absent();
-    private final ExecutorService handshakeExecutor;
-    private final char[] pem;
-    private volatile boolean up;
-
-    private NetconfSSHServer(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pem) throws IOException {
-        super(NetconfSSHServer.class.getSimpleName());
-        this.bossGroup = bossGroup;
-        this.pem = pem;
-        logger.trace("Creating SSH server socket on port {}", serverPort);
-        this.serverSocket = new ServerSocket(serverPort);
-        if (serverSocket.isBound() == false) {
-            throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort);
-        }
-        logger.trace("Server socket created.");
-        this.localAddress = localAddress;
-        this.up = true;
-        handshakeExecutor = Executors.newFixedThreadPool(10);
-    }
-
-    public static NetconfSSHServer start(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pemArray) throws IOException {
-        final NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, bossGroup, pemArray);
-        netconfSSHServer.start();
-        return netconfSSHServer;
-    }
-
-    public synchronized AuthProvider getAuthProvider() {
-        Preconditions.checkState(authProvider.isPresent(), "AuthenticationProvider is not set up, cannot authenticate user");
-        return authProvider.get();
-    }
-
-    public synchronized void setAuthProvider(final AuthProvider authProvider) {
-        if(this.authProvider != null) {
-            logger.debug("Changing auth provider to {}", authProvider);
-        }
-        this.authProvider = Optional.fromNullable(authProvider);
-    }
-
-    @Override
-    public void close() throws IOException {
-        up = false;
-        logger.trace("Closing SSH server socket.");
-        serverSocket.close();
-        bossGroup.shutdownGracefully();
-        logger.trace("SSH server socket closed.");
-    }
-
-    @VisibleForTesting
-    public InetSocketAddress getLocalSocketAddress() {
-        return (InetSocketAddress) serverSocket.getLocalSocketAddress();
-    }
-
-    @Override
-    public void run() {
-        while (up) {
-            Socket acceptedSocket = null;
-            try {
-                acceptedSocket = serverSocket.accept();
-            } catch (final IOException e) {
-                if (up == false) {
-                    logger.trace("Exiting server thread", e);
-                } else {
-                    logger.warn("Exception occurred during socket.accept", e);
-                }
-            }
-            if (acceptedSocket != null) {
-                try {
-                    final Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), getAuthProvider(), bossGroup, pem);
-                    handshakeExecutor.submit(task);
-                } catch (final IOException e) {
-                    logger.warn("Cannot set PEMHostKey, closing connection", e);
-                    closeSocket(acceptedSocket);
-                } catch (final IllegalStateException e) {
-                    logger.warn("Cannot accept connection, closing", e);
-                    closeSocket(acceptedSocket);
-                }
-            }
-        }
-        logger.debug("Server thread is exiting");
-    }
-
-    private void closeSocket(final Socket acceptedSocket) {
-        try {
-            acceptedSocket.close();
-        } catch (final IOException e) {
-            logger.warn("Ignoring exception while closing socket", e);
-        }
-    }
-
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java
new file mode 100644 (file)
index 0000000..e642e07
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.util.concurrent.GenericFutureListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.io.IoInputStream;
+import org.apache.sshd.common.io.IoOutputStream;
+import org.apache.sshd.server.AsyncCommand;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.session.ServerSession;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This command handles all netconf related rpc and forwards to delegate server.
+ * Uses netty to make a local connection to delegate server.
+ *
+ * Command is Apache Mina SSH terminology for objects handling ssh data.
+ */
+public class RemoteNetconfCommand implements AsyncCommand, SessionAware {
+
+    private static final Logger logger = LoggerFactory.getLogger(RemoteNetconfCommand.class);
+
+    private final EventLoopGroup clientEventGroup;
+    private final LocalAddress localAddress;
+
+    private IoInputStream in;
+    private IoOutputStream out;
+    private ExitCallback callback;
+    private NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
+
+    private Channel clientChannel;
+    private ChannelFuture clientChannelFuture;
+
+    public RemoteNetconfCommand(final EventLoopGroup clientEventGroup, final LocalAddress localAddress) {
+        this.clientEventGroup = clientEventGroup;
+        this.localAddress = localAddress;
+    }
+
+    @Override
+    public void setIoInputStream(final IoInputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public void setIoOutputStream(final IoOutputStream out) {
+        this.out = out;
+    }
+
+    @Override
+    public void setIoErrorStream(final IoOutputStream err) {
+        // TODO do we want to use error stream in some way ?
+    }
+
+    @Override
+    public void setInputStream(final InputStream in) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+    }
+
+    @Override
+    public void setOutputStream(final OutputStream out) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+
+    }
+
+    @Override
+    public void setErrorStream(final OutputStream err) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+
+    }
+
+    @Override
+    public void setExitCallback(final ExitCallback callback) {
+        this.callback = callback;
+    }
+
+    @Override
+    public void start(final Environment env) throws IOException {
+        logger.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
+
+        final Bootstrap clientBootstrap = new Bootstrap();
+        clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
+
+        clientBootstrap
+                .handler(new ChannelInitializer<LocalChannel>() {
+                    @Override
+                    public void initChannel(final LocalChannel ch) throws Exception {
+                        ch.pipeline().addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
+                    }
+                });
+        clientChannelFuture = clientBootstrap.connect(localAddress);
+        clientChannelFuture.addListener(new GenericFutureListener<ChannelFuture>() {
+
+            @Override
+            public void operationComplete(final ChannelFuture future) throws Exception {
+                if(future.isSuccess()) {
+                    clientChannel = clientChannelFuture.channel();
+                } else {
+                    logger.warn("Unable to establish internal connection to netconf server for client: {}", getClientAddress());
+                    Preconditions.checkNotNull(callback, "Exit callback must be set");
+                    callback.onExit(1, "Unable to establish internal connection to netconf server for client: "+ getClientAddress());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void destroy() {
+        logger.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
+                getClientAddress(), clientChannel);
+
+        clientChannelFuture.cancel(true);
+        if(clientChannel != null) {
+            clientChannel.close().addListener(new GenericFutureListener<ChannelFuture>() {
+
+                @Override
+                public void operationComplete(final ChannelFuture future) throws Exception {
+                    if (future.isSuccess() == false) {
+                        logger.warn("Unable to release internal connection to netconf server on channel: {}", clientChannel);
+                    }
+                }
+            });
+        }
+    }
+
+    private String getClientAddress() {
+        return netconfHelloMessageAdditionalHeader.getAddress();
+    }
+
+    @Override
+    public void setSession(final ServerSession session) {
+        final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
+        String hostName = "";
+        String port = "";
+        if(remoteAddress instanceof InetSocketAddress) {
+            hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
+            port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
+        }
+        netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(
+                session.getUsername(), hostName, port, "ssh", "client");
+    }
+
+    public static class NetconfCommandFactory implements NamedFactory<Command> {
+
+        public static final String NETCONF = "netconf";
+
+        private final EventLoopGroup clientBootstrap;
+        private final LocalAddress localAddress;
+
+        public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
+
+            this.clientBootstrap = clientBootstrap;
+            this.localAddress = localAddress;
+        }
+
+        @Override
+        public String getName() {
+            return NETCONF;
+        }
+
+        @Override
+        public RemoteNetconfCommand create() {
+            return new RemoteNetconfCommand(clientBootstrap, localAddress);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java
new file mode 100644 (file)
index 0000000..2b2b3b3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.sshd.common.io.IoInputStream;
+import org.apache.sshd.common.io.IoOutputStream;
+import org.apache.sshd.server.ExitCallback;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerWriter;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Netty handler that reads SSH from remote client and writes to delegate server and reads from delegate server and writes to remote client
+ */
+final class SshProxyClientHandler extends ChannelInboundHandlerAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(SshProxyClientHandler.class);
+
+    private final IoInputStream in;
+    private final IoOutputStream out;
+
+    private AsyncSshHandlerReader asyncSshHandlerReader;
+    private AsyncSshHandlerWriter asyncSshHandlerWriter;
+
+    private final NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
+    private final ExitCallback callback;
+
+    public SshProxyClientHandler(final IoInputStream in, final IoOutputStream out,
+                                 final NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader,
+                                 final ExitCallback callback) {
+        this.in = in;
+        this.out = out;
+        this.netconfHelloMessageAdditionalHeader = netconfHelloMessageAdditionalHeader;
+        this.callback = callback;
+    }
+
+    @Override
+    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
+        writeAdditionalHeader(ctx);
+
+        asyncSshHandlerWriter = new AsyncSshHandlerWriter(out);
+        asyncSshHandlerReader = new AsyncSshHandlerReader(new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                // Close both sessions (delegate server and remote client)
+                ctx.fireChannelInactive();
+                ctx.disconnect();
+                ctx.close();
+                asyncSshHandlerReader.close();
+                asyncSshHandlerWriter.close();
+            }
+        }, new AsyncSshHandlerReader.ReadMsgHandler() {
+            @Override
+            public void onMessageRead(final ByteBuf msg) {
+                if(logger.isTraceEnabled()) {
+                    logger.trace("Forwarding message for client: {} on channel: {}, message: {}",
+                            netconfHelloMessageAdditionalHeader.getAddress(), ctx.channel(), AsyncSshHandlerWriter.byteBufToString(msg));
+                }
+                // Just forward to delegate
+                ctx.writeAndFlush(msg);
+            }
+        }, "ssh" + netconfHelloMessageAdditionalHeader.getAddress(), in);
+
+
+        super.channelActive(ctx);
+    }
+
+    private void writeAdditionalHeader(final ChannelHandlerContext ctx) {
+        ctx.writeAndFlush(Unpooled.copiedBuffer(netconfHelloMessageAdditionalHeader.toFormattedString().getBytes()));
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
+       asyncSshHandlerWriter.write(ctx, msg, ctx.newPromise());
+    }
+
+    @Override
+    public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
+        logger.debug("Internal connection to netconf server was dropped for client: {} on channel: ",
+                netconfHelloMessageAdditionalHeader.getAddress(), ctx.channel());
+        callback.onExit(1, "Internal connection to netconf server was dropped for client: " +
+                netconfHelloMessageAdditionalHeader.getAddress() + " on channel: " + ctx.channel());
+        super.channelInactive(ctx);
+    }
+
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java
new file mode 100644 (file)
index 0000000..0b85cf2
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import com.google.common.collect.Lists;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.sshd.SshServer;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoConnector;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.io.IoServiceFactoryFactory;
+import org.apache.sshd.common.io.nio2.Nio2Acceptor;
+import org.apache.sshd.common.io.nio2.Nio2Connector;
+import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
+import org.apache.sshd.common.util.CloseableUtils;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.PasswordAuthenticator;
+
+/**
+ * Proxy SSH server that just delegates decrypted content to a delegate server within same VM.
+ * Implemented using Apache Mina SSH lib.
+ */
+public class SshProxyServer implements AutoCloseable {
+
+    private final SshServer sshServer;
+    private final ScheduledExecutorService minaTimerExecutor;
+    private final EventLoopGroup clientGroup;
+    private final IoServiceFactoryFactory nioServiceWithPoolFactoryFactory;
+
+    public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup, final ExecutorService nioExecutor) {
+        this.minaTimerExecutor = minaTimerExecutor;
+        this.clientGroup = clientGroup;
+        this.nioServiceWithPoolFactoryFactory = new NioServiceWithPoolFactory.NioServiceWithPoolFactoryFactory(nioExecutor);
+        this.sshServer = SshServer.setUpDefaultServer();
+    }
+
+    public void bind(final InetSocketAddress bindingAddress, final LocalAddress localAddress, final PasswordAuthenticator authenticator, final KeyPairProvider keyPairProvider) throws IOException {
+        sshServer.setHost(bindingAddress.getHostString());
+        sshServer.setPort(bindingAddress.getPort());
+
+        sshServer.setPasswordAuthenticator(authenticator);
+        sshServer.setKeyPairProvider(keyPairProvider);
+
+        sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory);
+        sshServer.setScheduledExecutorService(minaTimerExecutor);
+
+        final RemoteNetconfCommand.NetconfCommandFactory netconfCommandFactory =
+                new RemoteNetconfCommand.NetconfCommandFactory(clientGroup, localAddress);
+        sshServer.setSubsystemFactories(Lists.<NamedFactory<Command>>newArrayList(netconfCommandFactory));
+        sshServer.start();
+    }
+
+    @Override
+    public void close() {
+        try {
+            sshServer.stop(true);
+        } catch (final InterruptedException e) {
+            throw new RuntimeException("Interrupted while stopping sshServer", e);
+        } finally {
+            sshServer.close(true);
+        }
+    }
+
+    /**
+     * Based on Nio2ServiceFactory with one addition: injectable executor
+     */
+    private static final class NioServiceWithPoolFactory extends CloseableUtils.AbstractCloseable implements IoServiceFactory {
+
+        private final FactoryManager manager;
+        private final AsynchronousChannelGroup group;
+
+        public NioServiceWithPoolFactory(final FactoryManager manager, final ExecutorService executor) {
+            this.manager = manager;
+            try {
+                group = AsynchronousChannelGroup.withThreadPool(executor);
+            } catch (final IOException e) {
+                throw new RuntimeSshException(e);
+            }
+        }
+
+        public IoConnector createConnector(final IoHandler handler) {
+            return new Nio2Connector(manager, handler, group);
+        }
+
+        public IoAcceptor createAcceptor(final IoHandler handler) {
+            return new Nio2Acceptor(manager, handler, group);
+        }
+
+        @Override
+        protected void doCloseImmediately() {
+            try {
+                group.shutdownNow();
+                group.awaitTermination(5, TimeUnit.SECONDS);
+            } catch (final Exception e) {
+                log.debug("Exception caught while closing channel group", e);
+            } finally {
+                super.doCloseImmediately();
+            }
+        }
+
+        private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory {
+
+            private final ExecutorService nioExecutor;
+
+            private NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) {
+                this.nioExecutor = nioExecutor;
+            }
+
+            @Override
+            public IoServiceFactory create(final FactoryManager manager) {
+                return new NioServiceWithPoolFactory(manager, nioExecutor);
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java
deleted file mode 100644 (file)
index 53ab821..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.netconf.ssh.authentication;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.io.FileInputStream;
-import java.security.NoSuchAlgorithmException;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.bouncycastle.openssl.PEMWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.SecureRandom;
-
-public class PEMGenerator {
-    private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
-    private static final int KEY_SIZE = 4096;
-
-
-    public static String readOrGeneratePK(File privateKeyFile) throws IOException {
-        if (privateKeyFile.exists() == false) {
-            // generate & save to file
-            try {
-                return generateTo(privateKeyFile);
-            } catch (Exception e) {
-                logger.error("Exception occurred while generating PEM string to {}", privateKeyFile, e);
-                throw new IllegalStateException("Error generating RSA key from file " + privateKeyFile);
-            }
-        } else {
-            // read from file
-            try (FileInputStream fis = new FileInputStream(privateKeyFile)) {
-                return IOUtils.toString(fis);
-            } catch (final IOException e) {
-                logger.error("Error reading RSA key from file {}", privateKeyFile, e);
-                throw new IOException("Error reading RSA key from file " + privateKeyFile, e);
-            }
-        }
-    }
-
-    /**
-     * Generate private key to a file and return its content as string.
-     *
-     * @param privateFile path where private key should be generated
-     * @return String representation of private key
-     * @throws IOException
-     * @throws NoSuchAlgorithmException
-     */
-    @VisibleForTesting
-    public static String generateTo(File privateFile) throws IOException, NoSuchAlgorithmException {
-        logger.info("Generating private key to {}", privateFile.getAbsolutePath());
-        String privatePEM = generate();
-        FileUtils.write(privateFile, privatePEM);
-        return privatePEM;
-    }
-
-    @VisibleForTesting
-    public static String generate() throws NoSuchAlgorithmException, IOException {
-        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
-        SecureRandom sr = new SecureRandom();
-        keyGen.initialize(KEY_SIZE, sr);
-        KeyPair keypair = keyGen.generateKeyPair();
-        return toString(keypair.getPrivate());
-    }
-
-    /**
-     * Get string representation of a key.
-     */
-    private static String toString(Key key) throws IOException {
-        try (StringWriter writer = new StringWriter()) {
-            try (PEMWriter pemWriter = new PEMWriter(writer)) {
-                pemWriter.writeObject(key);
-            }
-            return writer.toString();
-        }
-    }
-
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java
new file mode 100644 (file)
index 0000000..97e611c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh.osgi;
+
+import com.google.common.base.Preconditions;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.opendaylight.controller.netconf.auth.AuthConstants;
+import org.opendaylight.controller.netconf.auth.AuthProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class AuthProviderTracker implements ServiceTrackerCustomizer<AuthProvider, AuthProvider>, PasswordAuthenticator {
+    private static final Logger logger = LoggerFactory.getLogger(AuthProviderTracker.class);
+
+    private final BundleContext bundleContext;
+
+    private Integer maxPreference;
+    private final ServiceTracker<AuthProvider, AuthProvider> listenerTracker;
+    private AuthProvider authProvider;
+
+    public AuthProviderTracker(final BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+        listenerTracker = new ServiceTracker<>(bundleContext, AuthProvider.class, this);
+        listenerTracker.open();
+    }
+
+    @Override
+    public AuthProvider addingService(final ServiceReference<AuthProvider> reference) {
+        logger.trace("Service {} added", reference);
+        final AuthProvider authService = bundleContext.getService(reference);
+        final Integer newServicePreference = getPreference(reference);
+        if(isBetter(newServicePreference)) {
+            maxPreference = newServicePreference;
+            this.authProvider = authService;
+        }
+        return authService;
+    }
+
+    private Integer getPreference(final ServiceReference<AuthProvider> reference) {
+        final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY);
+        return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString());
+    }
+
+    private boolean isBetter(final Integer newServicePreference) {
+        Preconditions.checkNotNull(newServicePreference);
+        if(maxPreference == null) {
+            return true;
+        }
+
+        return newServicePreference > maxPreference;
+    }
+
+    @Override
+    public void modifiedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
+        final AuthProvider authService = bundleContext.getService(reference);
+        final Integer newServicePreference = getPreference(reference);
+        if(isBetter(newServicePreference)) {
+            logger.trace("Replacing modified service {} in netconf SSH.", reference);
+            this.authProvider = authService;
+        }
+    }
+
+    @Override
+    public void removedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
+        logger.trace("Removing service {} from netconf SSH. " +
+                "SSH won't authenticate users until AuthProvider service will be started.", reference);
+        maxPreference = null;
+        this.authProvider = null;
+    }
+
+    public void stop() {
+        listenerTracker.close();
+        // sshThread should finish normally since sshServer.close stops processing
+    }
+
+    @Override
+    public boolean authenticate(final String username, final String password, final ServerSession session) {
+        return authProvider == null ? false : authProvider.authenticated(username, password);
+    }
+}
index 0d0f95c3cb4d0856ba56c470f0890eb51371caa1..b871d19db8f062d0dbca93cc18ce6c3c6e09ba2d 100644 (file)
@@ -9,51 +9,51 @@ package org.opendaylight.controller.netconf.ssh.osgi;
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.common.base.Preconditions;
-import java.io.File;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
 import org.apache.commons.io.FilenameUtils;
-import org.opendaylight.controller.netconf.auth.AuthConstants;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Strings;
-
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.channel.nio.NioEventLoopGroup;
-
-/**
- * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
- * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
- * and listens for client connections. Each client connection creation is handled in separate
- * {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker} thread.
- * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
- * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
- * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
- * All threads are daemons.
- */
 public class NetconfSSHActivator implements BundleActivator {
     private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class);
-    private static AuthProviderTracker authProviderTracker;
 
-    private NetconfSSHServer server;
+    private static final java.lang.String ALGORITHM = "RSA";
+    private static final int KEY_SIZE = 4096;
+    public static final int POOL_SIZE = 8;
+
+    private ScheduledExecutorService minaTimerExecutor;
+    private NioEventLoopGroup clientGroup;
+    private ExecutorService nioExecutor;
+    private AuthProviderTracker authProviderTracker;
+
+    private SshProxyServer server;
 
     @Override
     public void start(final BundleContext bundleContext) throws IOException {
+        minaTimerExecutor = Executors.newScheduledThreadPool(POOL_SIZE, new ThreadFactory() {
+            @Override
+            public Thread newThread(final Runnable r) {
+                return new Thread(r, "netconf-ssh-server-mina-timers");
+            }
+        });
+        clientGroup = new NioEventLoopGroup();
+        nioExecutor = ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", POOL_SIZE);
         server = startSSHServer(bundleContext);
     }
 
@@ -66,11 +66,22 @@ public class NetconfSSHActivator implements BundleActivator {
         if(authProviderTracker != null) {
             authProviderTracker.stop();
         }
+
+        if(nioExecutor!=null) {
+            nioExecutor.shutdownNow();
+        }
+
+        if(clientGroup != null) {
+            clientGroup.shutdownGracefully();
+        }
+
+        if(minaTimerExecutor != null) {
+            minaTimerExecutor.shutdownNow();
+        }
     }
 
-    private static NetconfSSHServer startSSHServer(final BundleContext bundleContext) throws IOException {
-        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext,
-                InfixProp.ssh);
+    private SshProxyServer startSSHServer(final BundleContext bundleContext) throws IOException {
+        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, InfixProp.ssh);
 
         if (maybeSshSocketAddress.isPresent() == false) {
             logger.trace("SSH bridge not configured");
@@ -82,92 +93,15 @@ public class NetconfSSHActivator implements BundleActivator {
 
         final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
 
-        final String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
-        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
-        final String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
-
-        final EventLoopGroup bossGroup  = new NioEventLoopGroup();
-        final NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, bossGroup, privateKeyPEMString.toCharArray());
-
-        authProviderTracker = new AuthProviderTracker(bundleContext, server);
+        authProviderTracker = new AuthProviderTracker(bundleContext);
 
-        return server;
-    }
+        final String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
+        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s",
+                NetconfConfigUtil.getPrivateKeyKey());
 
-    private static Thread runNetconfSshThread(final NetconfSSHServer server) {
-        final Thread serverThread = new Thread(server, "netconf SSH server thread");
-        serverThread.setDaemon(true);
-        serverThread.start();
-        logger.trace("Netconf SSH  bridge up and running.");
-        return serverThread;
+        final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerExecutor, clientGroup, nioExecutor);
+        sshProxyServer.bind(sshSocketAddress, localAddress, authProviderTracker, new PEMGeneratorHostKeyProvider(path, ALGORITHM, KEY_SIZE));
+        return sshProxyServer;
     }
 
-    private static class AuthProviderTracker implements ServiceTrackerCustomizer<AuthProvider, AuthProvider> {
-        private final BundleContext bundleContext;
-        private final NetconfSSHServer server;
-
-        private Integer maxPreference;
-        private Thread sshThread;
-        private final ServiceTracker<AuthProvider, AuthProvider> listenerTracker;
-
-        public AuthProviderTracker(final BundleContext bundleContext, final NetconfSSHServer server) {
-            this.bundleContext = bundleContext;
-            this.server = server;
-            listenerTracker = new ServiceTracker<>(bundleContext, AuthProvider.class, this);
-            listenerTracker.open();
-        }
-
-        @Override
-        public AuthProvider addingService(final ServiceReference<AuthProvider> reference) {
-            logger.trace("Service {} added", reference);
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                maxPreference = newServicePreference;
-                server.setAuthProvider(authService);
-                if(sshThread == null) {
-                    sshThread = runNetconfSshThread(server);
-                }
-            }
-            return authService;
-        }
-
-        private Integer getPreference(final ServiceReference<AuthProvider> reference) {
-            final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY);
-            return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString());
-        }
-
-        private boolean isBetter(final Integer newServicePreference) {
-            Preconditions.checkNotNull(newServicePreference);
-            if(maxPreference == null) {
-                return true;
-            }
-
-            return newServicePreference > maxPreference;
-        }
-
-        @Override
-        public void modifiedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                logger.trace("Replacing modified service {} in netconf SSH.", reference);
-                server.setAuthProvider(authService);
-            }
-        }
-
-        @Override
-        public void removedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            logger.trace("Removing service {} from netconf SSH. " +
-                    "SSH won't authenticate users until AuthProvider service will be started.", reference);
-            maxPreference = null;
-            server.setAuthProvider(null);
-        }
-
-        public void stop() {
-            listenerTracker.close();
-            // sshThread should finish normally since sshServer.close stops processing
-        }
-
-    }
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java
deleted file mode 100644 (file)
index eec6c3a..0000000
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.ssh.threads;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import ch.ethz.ssh2.AuthenticationResult;
-import ch.ethz.ssh2.PtySettings;
-import ch.ethz.ssh2.ServerAuthenticationCallback;
-import ch.ethz.ssh2.ServerConnection;
-import ch.ethz.ssh2.ServerConnectionCallback;
-import ch.ethz.ssh2.ServerSession;
-import ch.ethz.ssh2.ServerSessionCallback;
-import ch.ethz.ssh2.SimpleServerSessionCallback;
-
-import com.google.common.base.Supplier;
-
-import io.netty.bootstrap.Bootstrap;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufProcessor;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.channel.local.LocalChannel;
-import io.netty.handler.stream.ChunkedStream;
-
-/**
- * One instance represents per connection, responsible for ssh handshake.
- * Once auth succeeds and correct subsystem is chosen, backend connection with
- * netty netconf server is made. This task finishes right after negotiation is done.
- */
-@ThreadSafe
-public class Handshaker implements Runnable {
-    private static final Logger logger = LoggerFactory.getLogger(Handshaker.class);
-
-    private final ServerConnection ganymedConnection;
-    private final String session;
-
-
-    public Handshaker(Socket socket, LocalAddress localAddress, long sessionId, AuthProvider authProvider,
-                      EventLoopGroup bossGroup, final char[] pem) throws IOException {
-
-        this.session = "Session " + sessionId;
-
-        String remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replace("/", "");
-        logger.debug("{} started with {}", session, remoteAddressWithPort);
-        String remoteAddress, remotePort;
-        if (remoteAddressWithPort.contains(":")) {
-            String[] split = remoteAddressWithPort.split(":");
-            remoteAddress = split[0];
-            remotePort = split[1];
-        } else {
-            remoteAddress = remoteAddressWithPort;
-            remotePort = "";
-        }
-        ServerAuthenticationCallbackImpl serverAuthenticationCallback = new ServerAuthenticationCallbackImpl(
-                authProvider, session);
-
-        ganymedConnection = new ServerConnection(socket);
-
-        ServerConnectionCallbackImpl serverConnectionCallback = new ServerConnectionCallbackImpl(
-                serverAuthenticationCallback, remoteAddress, remotePort, session,
-                getGanymedAutoCloseable(ganymedConnection), localAddress, bossGroup);
-
-        // initialize ganymed
-        ganymedConnection.setPEMHostKey(pem, null);
-        ganymedConnection.setAuthenticationCallback(serverAuthenticationCallback);
-        ganymedConnection.setServerConnectionCallback(serverConnectionCallback);
-    }
-
-
-    private static AutoCloseable getGanymedAutoCloseable(final ServerConnection ganymedConnection) {
-        return new AutoCloseable() {
-            @Override
-            public void close() throws Exception {
-                ganymedConnection.close();
-            }
-        };
-    }
-
-    @Override
-    public void run() {
-        // let ganymed process handshake
-        logger.trace("{} is started", session);
-        try {
-            // TODO this should be guarded with a timer to prevent resource exhaustion
-            ganymedConnection.connect();
-        } catch (IOException e) {
-            logger.debug("{} connection error", session, e);
-        }
-        logger.trace("{} is exiting", session);
-    }
-}
-
-/**
- * Netty client handler that forwards bytes from backed server to supplied output stream.
- * When backend server closes the connection, remoteConnection.close() is called to tear
- * down ssh connection.
- */
-class SSHClientHandler extends ChannelInboundHandlerAdapter {
-    private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class);
-    private final AutoCloseable remoteConnection;
-    private final BufferedOutputStream remoteOutputStream;
-    private final String session;
-    private ChannelHandlerContext channelHandlerContext;
-
-    public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream,
-                            String session) {
-        this.remoteConnection = remoteConnection;
-        this.remoteOutputStream = new BufferedOutputStream(remoteOutputStream);
-        this.session = session;
-    }
-
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) {
-        this.channelHandlerContext = ctx;
-        logger.debug("{} Client active", session);
-    }
-
-    @Override
-    public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
-        ByteBuf bb = (ByteBuf) msg;
-        // we can block the server here so that slow client does not cause memory pressure
-        try {
-            bb.forEachByte(new ByteBufProcessor() {
-                @Override
-                public boolean process(byte value) throws Exception {
-                    remoteOutputStream.write(value);
-                    return true;
-                }
-            });
-        } finally {
-            bb.release();
-        }
-    }
-
-    @Override
-    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
-        logger.trace("{} Flushing", session);
-        remoteOutputStream.flush();
-    }
-
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
-        // Close the connection when an exception is raised.
-        logger.warn("{} Unexpected exception from downstream", session, cause);
-        ctx.close();
-    }
-
-    @Override
-    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        logger.trace("{} channelInactive() called, closing remote client ctx", session);
-        remoteConnection.close();//this should close socket and all threads created for this client
-        this.channelHandlerContext = null;
-    }
-
-    public ChannelHandlerContext getChannelHandlerContext() {
-        return checkNotNull(channelHandlerContext, "Channel is not active");
-    }
-}
-
-/**
- * Ganymed handler that gets unencrypted input and output streams, connects them to netty.
- * Checks that 'netconf' subsystem is chosen by user.
- * Launches new ClientInputStreamPoolingThread thread once session is established.
- * Writes custom header to netty server, to inform it about IP address and username.
- */
-class ServerConnectionCallbackImpl implements ServerConnectionCallback {
-    private static final Logger logger = LoggerFactory.getLogger(ServerConnectionCallbackImpl.class);
-    public static final String NETCONF_SUBSYSTEM = "netconf";
-
-    private final Supplier<String> currentUserSupplier;
-    private final String remoteAddress;
-    private final String remotePort;
-    private final String session;
-    private final AutoCloseable ganymedConnection;
-    private final LocalAddress localAddress;
-    private final EventLoopGroup bossGroup;
-
-    ServerConnectionCallbackImpl(Supplier<String> currentUserSupplier, String remoteAddress, String remotePort, String session,
-                                 AutoCloseable ganymedConnection, LocalAddress localAddress, EventLoopGroup bossGroup) {
-        this.currentUserSupplier = currentUserSupplier;
-        this.remoteAddress = remoteAddress;
-        this.remotePort = remotePort;
-        this.session = session;
-        this.ganymedConnection = ganymedConnection;
-        // initialize netty local connection
-        this.localAddress = localAddress;
-        this.bossGroup = bossGroup;
-    }
-
-    private static ChannelFuture initializeNettyConnection(LocalAddress localAddress, EventLoopGroup bossGroup,
-                                                           final SSHClientHandler sshClientHandler) {
-        Bootstrap clientBootstrap = new Bootstrap();
-        clientBootstrap.group(bossGroup).channel(LocalChannel.class);
-
-        clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
-            @Override
-            public void initChannel(LocalChannel ch) throws Exception {
-                ch.pipeline().addLast(sshClientHandler);
-            }
-        });
-        // asynchronously initialize local connection to netconf server
-        return clientBootstrap.connect(localAddress);
-    }
-
-    @Override
-    public ServerSessionCallback acceptSession(final ServerSession serverSession) {
-        String currentUser = currentUserSupplier.get();
-        final String additionalHeader = new NetconfHelloMessageAdditionalHeader(currentUser, remoteAddress,
-                remotePort, "ssh", "client").toFormattedString();
-
-
-        return new SimpleServerSessionCallback() {
-            @Override
-            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        if (NETCONF_SUBSYSTEM.equals(subsystem)) {
-                            // connect
-                            final SSHClientHandler sshClientHandler = new SSHClientHandler(ganymedConnection, ss.getStdin(), session);
-                            ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler);
-                            // get channel
-                            final Channel channel = clientChannelFuture.awaitUninterruptibly().channel();
-
-                            // write additional header before polling thread is started
-                            // polling thread could process and forward data before additional header is written
-                            // This will result into unexpected state:  hello message without additional header and the next message with additional header
-                            channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes()));
-
-                            new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() {
-                                @Override
-                                public void close() throws Exception {
-                                    logger.trace("Closing both ganymed and local connection");
-                                    try {
-                                        ganymedConnection.close();
-                                    } catch (Exception e) {
-                                        logger.warn("Ignoring exception while closing ganymed", e);
-                                    }
-                                    try {
-                                        channel.close();
-                                    } catch (Exception e) {
-                                        logger.warn("Ignoring exception while closing channel", e);
-                                    }
-                                }
-                            }, sshClientHandler.getChannelHandlerContext()).start();
-                        } else {
-                            logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem);
-                            String reason = "Only netconf subsystem is supported, requested:" + subsystem;
-                            closeSession(ss, reason);
-                        }
-                    }
-                };
-            }
-
-            public void closeSession(ServerSession ss, String reason) {
-                logger.trace("{} Closing session - {}", serverSession, reason);
-                try {
-                    ss.getStdin().write(reason.getBytes());
-                } catch (IOException e) {
-                    logger.warn("{} Exception while closing session", serverSession, e);
-                }
-                ss.close();
-            }
-
-            @Override
-            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "PTY request not supported");
-                    }
-                };
-            }
-
-            @Override
-            public Runnable requestShell(final ServerSession ss) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "Shell not supported");
-                    }
-                };
-            }
-        };
-    }
-}
-
-/**
- * Only thread that is required during ssh session, forwards client's input to netty.
- * When user closes connection, onEndOfInput.close() is called to tear down the local channel.
- */
-class ClientInputStreamPoolingThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(ClientInputStreamPoolingThread.class);
-
-    private final InputStream fromClientIS;
-    private final Channel serverChannel;
-    private final AutoCloseable onEndOfInput;
-    private final ChannelHandlerContext channelHandlerContext;
-
-    ClientInputStreamPoolingThread(String session, InputStream fromClientIS, Channel serverChannel, AutoCloseable onEndOfInput,
-                                   ChannelHandlerContext channelHandlerContext) {
-        super(ClientInputStreamPoolingThread.class.getSimpleName() + " " + session);
-        this.fromClientIS = fromClientIS;
-        this.serverChannel = serverChannel;
-        this.onEndOfInput = onEndOfInput;
-        this.channelHandlerContext = channelHandlerContext;
-    }
-
-    @Override
-    public void run() {
-        ChunkedStream chunkedStream = new ChunkedStream(fromClientIS);
-        try {
-            ByteBuf byteBuf;
-            while ((byteBuf = chunkedStream.readChunk(channelHandlerContext/*only needed for ByteBuf alloc */)) != null) {
-                serverChannel.writeAndFlush(byteBuf);
-            }
-        } catch (Exception e) {
-            logger.warn("Exception", e);
-        } finally {
-            logger.trace("End of input");
-            // tear down connection
-            try {
-                onEndOfInput.close();
-            } catch (Exception e) {
-                logger.warn("Ignoring exception while closing socket", e);
-            }
-        }
-    }
-}
-
-/**
- * Authentication handler for ganymed.
- * Provides current user name after authenticating using supplied AuthProvider.
- */
-@NotThreadSafe
-class ServerAuthenticationCallbackImpl implements ServerAuthenticationCallback, Supplier<String> {
-    private static final Logger logger = LoggerFactory.getLogger(ServerAuthenticationCallbackImpl.class);
-    private final AuthProvider authProvider;
-    private final String session;
-    private String currentUser;
-
-    ServerAuthenticationCallbackImpl(AuthProvider authProvider, String session) {
-        this.authProvider = authProvider;
-        this.session = session;
-    }
-
-    @Override
-    public String initAuthentication(ServerConnection sc) {
-        logger.trace("{} Established connection", session);
-        return "Established connection" + "\r\n";
-    }
-
-    @Override
-    public String[] getRemainingAuthMethods(ServerConnection sc) {
-        return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
-        checkState(currentUser == null);
-        try {
-            if (authProvider.authenticated(username, password)) {
-                currentUser = username;
-                logger.trace("{} user {} authenticated", session, currentUser);
-                return AuthenticationResult.SUCCESS;
-            }
-        } catch (Exception e) {
-            logger.warn("{} Authentication failed", session, e);
-        }
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
-                                                          byte[] publicKey, byte[] signature) {
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public String get() {
-        return currentUser;
-    }
-}
index eb2b644cbca1fbaa5f98c2b704d3893ac55e9fa4..62ce58723765231d2c4edd8683cb5e18093c09fe 100644 (file)
@@ -11,9 +11,6 @@ package org.opendaylight.controller.netconf.netty;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 
 import com.google.common.base.Stopwatch;
 import io.netty.bootstrap.Bootstrap;
@@ -23,16 +20,21 @@ import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.util.HashedWheelTimer;
 import java.net.InetSocketAddress;
+import java.nio.file.Files;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
 import org.opendaylight.controller.netconf.netty.EchoClientHandler.State;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,68 +42,77 @@ import org.slf4j.LoggerFactory;
 public class SSHTest {
     public static final Logger logger = LoggerFactory.getLogger(SSHTest.class);
     public static final String AHOJ = "ahoj\n";
-    private EventLoopGroup nettyGroup;
-    HashedWheelTimer hashedWheelTimer;
 
-    @Before
-    public void setUp() throws Exception {
+    private static EventLoopGroup nettyGroup;
+    private static HashedWheelTimer hashedWheelTimer;
+    private static ExecutorService nioExec;
+    private static ScheduledExecutorService minaTimerEx;
+
+    @BeforeClass
+    public static void setUp() throws Exception {
         hashedWheelTimer = new HashedWheelTimer();
         nettyGroup = new NioEventLoopGroup();
+        nioExec = Executors.newFixedThreadPool(1);
+        minaTimerEx = Executors.newScheduledThreadPool(1);
     }
 
-    @After
-    public void tearDown() throws Exception {
+    @AfterClass
+    public static void tearDown() throws Exception {
         hashedWheelTimer.stop();
-        nettyGroup.shutdownGracefully();
+        nettyGroup.shutdownGracefully().await();
+        minaTimerEx.shutdownNow();
+        nioExec.shutdownNow();
     }
 
     @Test
     public void test() throws Exception {
         new Thread(new EchoServer(), "EchoServer").start();
-        AuthProvider authProvider = mock(AuthProvider.class);
-        doReturn(true).when(authProvider).authenticated(anyString(), anyString());
-        doReturn("auth").when(authProvider).toString();
-
-        NetconfSSHServer netconfSSHServer = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(),
-                new NioEventLoopGroup(), PEMGenerator.generate().toCharArray());
-        netconfSSHServer.setAuthProvider(authProvider);
 
-        InetSocketAddress address = netconfSSHServer.getLocalSocketAddress();
+        final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 10831);
+        final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerEx, nettyGroup, nioExec);
+        sshProxyServer.bind(addr, NetconfConfigUtil.getNetconfLocalAddress(),
+                new PasswordAuthenticator() {
+                    @Override
+                    public boolean authenticate(final String username, final String password, final ServerSession session) {
+                        return true;
+                    }
+                }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
 
-        final EchoClientHandler echoClientHandler = connectClient(new InetSocketAddress("localhost", address.getPort()));
+        final EchoClientHandler echoClientHandler = connectClient(addr);
 
         Stopwatch stopwatch = new Stopwatch().start();
-        while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
-            Thread.sleep(100);
+        while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 30) {
+            Thread.sleep(500);
         }
         assertTrue(echoClientHandler.isConnected());
         logger.info("connected, writing to client");
         echoClientHandler.write(AHOJ);
+
         // check that server sent back the same string
         stopwatch = stopwatch.reset().start();
-        while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
-            Thread.sleep(100);
+        while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 30) {
+            Thread.sleep(500);
         }
+
         try {
-            String read = echoClientHandler.read();
+            final String read = echoClientHandler.read();
             assertTrue(read + " should end with " + AHOJ, read.endsWith(AHOJ));
         } finally {
             logger.info("Closing socket");
-            netconfSSHServer.close();
-            netconfSSHServer.join();
+            sshProxyServer.close();
         }
     }
 
-    public EchoClientHandler connectClient(InetSocketAddress address) {
+    public EchoClientHandler connectClient(final InetSocketAddress address) {
         final EchoClientHandler echoClientHandler = new EchoClientHandler();
-        ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
+        final ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
             @Override
-            public void initChannel(NioSocketChannel ch) throws Exception {
+            public void initChannel(final NioSocketChannel ch) throws Exception {
                 ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
                 ch.pipeline().addLast(echoClientHandler);
             }
         };
-        Bootstrap b = new Bootstrap();
+        final Bootstrap b = new Bootstrap();
 
         b.group(nettyGroup)
                 .channel(NioSocketChannel.class)
@@ -114,9 +125,9 @@ public class SSHTest {
 
     @Test
     public void testClientWithoutServer() throws Exception {
-        InetSocketAddress address = new InetSocketAddress(12345);
+        final InetSocketAddress address = new InetSocketAddress(12345);
         final EchoClientHandler echoClientHandler = connectClient(address);
-        Stopwatch stopwatch = new Stopwatch().start();
+        final Stopwatch stopwatch = new Stopwatch().start();
         while(echoClientHandler.getState() == State.CONNECTING && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
             Thread.sleep(100);
         }
index 1151abcdf2767de1aa5a70fd19aa3219f9b23b1c..9cd0c9bceab59cb56df91afbb2c26c8421eeea4b 100644 (file)
@@ -12,19 +12,26 @@ import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 
-import ch.ethz.ssh2.Connection;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
-import java.io.InputStream;
 import java.net.InetSocketAddress;
-import junit.framework.Assert;
-import org.apache.commons.io.IOUtils;
+import java.nio.file.Files;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceListener;
@@ -39,13 +46,15 @@ public class SSHServerTest {
     private static final String PASSWORD = "netconf";
     private static final String HOST = "127.0.0.1";
     private static final int PORT = 1830;
-    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
     private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class);
-    private Thread sshServerThread;
+
+    private SshProxyServer server;
 
     @Mock
     private BundleContext mockedContext;
-
+    private final ExecutorService nioExec = Executors.newFixedThreadPool(1);
+    private final EventLoopGroup clientGroup = new NioEventLoopGroup();
+    private final ScheduledExecutorService minaTimerEx = Executors.newScheduledThreadPool(1);
 
     @Before
     public void setUp() throws Exception {
@@ -55,42 +64,39 @@ public class SSHServerTest {
         doReturn(new ServiceReference[0]).when(mockedContext).getServiceReferences(anyString(), anyString());
 
         logger.info("Creating SSH server");
-        String pem;
-        try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
-            pem = IOUtils.toString(is);
-        }
 
-
-        EventLoopGroup bossGroup = new NioEventLoopGroup();
-        NetconfSSHServer server = NetconfSSHServer.start(PORT, NetconfConfigUtil.getNetconfLocalAddress(),
-                bossGroup, pem.toCharArray());
-        server.setAuthProvider(new AuthProvider() {
-            @Override
-            public boolean authenticated(final String username, final String password) {
-                return true;
-            }
-        });
-
-        sshServerThread = new Thread(server);
-        sshServerThread.setDaemon(true);
-        sshServerThread.start();
-        logger.info("SSH server on " + PORT);
+        final InetSocketAddress addr = InetSocketAddress.createUnresolved(HOST, PORT);
+        server = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
+        server.bind(addr, NetconfConfigUtil.getNetconfLocalAddress(),
+                new PasswordAuthenticator() {
+                    @Override
+                    public boolean authenticate(final String username, final String password, final ServerSession session) {
+                        return true;
+                    }
+                }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
+        logger.info("SSH server started on " + PORT);
     }
 
     @Test
-    public void connect() {
+    public void connect() throws Exception {
+        final SshClient sshClient = SshClient.setUpDefaultClient();
+        sshClient.start();
         try {
-            Connection conn = new Connection(HOST, PORT);
-            Assert.assertNotNull(conn);
-            logger.info("connecting to SSH server");
-            conn.connect();
-            logger.info("authenticating ...");
-            boolean isAuthenticated = conn.authenticateWithPassword(USER, PASSWORD);
-            Assert.assertTrue(isAuthenticated);
-        } catch (Exception e) {
-            logger.error("Error while starting SSH server.", e);
+            final ConnectFuture connect = sshClient.connect(USER, HOST, PORT);
+            connect.await(30, TimeUnit.SECONDS);
+            org.junit.Assert.assertTrue(connect.isConnected());
+            final ClientSession session = connect.getSession();
+            session.addPasswordIdentity(PASSWORD);
+            final AuthFuture auth = session.auth();
+            auth.await(30, TimeUnit.SECONDS);
+            org.junit.Assert.assertTrue(auth.isSuccess());
+        } finally {
+            sshClient.close(true);
+            server.close();
+            clientGroup.shutdownGracefully().await();
+            minaTimerEx.shutdownNow();
+            nioExec.shutdownNow();
         }
-
     }
 
 }
index d0939a288f3ee0bb85915675e64f441165af8731..e8ba769da547f552dc3fe47aaa9a6e3f35ad0d54 100644 (file)
@@ -19,13 +19,13 @@ import com.google.common.collect.Sets;
 import com.google.common.io.CharStreams;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.local.LocalAddress;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
 import java.io.Closeable;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -34,6 +34,8 @@ import java.net.Inet4Address;
 import java.net.InetSocketAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.AbstractMap;
 import java.util.Date;
 import java.util.HashMap;
@@ -42,8 +44,15 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
@@ -58,8 +67,7 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
 import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
@@ -85,14 +93,22 @@ public class NetconfDeviceSimulator implements Closeable {
     private final NioEventLoopGroup nettyThreadgroup;
     private final HashedWheelTimer hashedWheelTimer;
     private final List<Channel> devicesChannels = Lists.newArrayList();
+    private final List<SshProxyServer> sshWrappers = Lists.newArrayList();
+    private final ScheduledExecutorService minaTimerExecutor;
+    private final ExecutorService nioExecutor;
 
     public NetconfDeviceSimulator() {
-        this(new NioEventLoopGroup(), new HashedWheelTimer());
+        // TODO make pool size configurable
+        this(new NioEventLoopGroup(), new HashedWheelTimer(),
+                Executors.newScheduledThreadPool(8, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()),
+                ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", 8));
     }
 
-    public NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer) {
+    private NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer, final ScheduledExecutorService minaTimerExecutor, final ExecutorService nioExecutor) {
         this.nettyThreadgroup = eventExecutors;
         this.hashedWheelTimer = hashedWheelTimer;
+        this.minaTimerExecutor = minaTimerExecutor;
+        this.nioExecutor = nioExecutor;
     }
 
     private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout) {
@@ -162,17 +178,31 @@ public class NetconfDeviceSimulator implements Closeable {
         int currentPort = params.startingPort;
 
         final List<Integer> openDevices = Lists.newArrayList();
+
+        // Generate key to temp folder
+        final PEMGeneratorHostKeyProvider keyPairProvider = getPemGeneratorHostKeyProvider();
+
         for (int i = 0; i < params.deviceCount; i++) {
             final InetSocketAddress address = getAddress(currentPort);
 
             final ChannelFuture server;
             if(params.ssh) {
+                final InetSocketAddress bindingAddress = InetSocketAddress.createUnresolved("0.0.0.0", currentPort);
                 final LocalAddress tcpLocalAddress = new LocalAddress(address.toString());
 
                 server = dispatcher.createLocalServer(tcpLocalAddress);
                 try {
-                    final NetconfSSHServer sshServer = NetconfSSHServer.start(currentPort, tcpLocalAddress, nettyThreadgroup, getPemArray());
-                    sshServer.setAuthProvider(new AcceptingAuthProvider());
+                    final SshProxyServer sshServer = new SshProxyServer(minaTimerExecutor, nettyThreadgroup, nioExecutor);
+                    sshServer.bind(bindingAddress, tcpLocalAddress,
+                            new PasswordAuthenticator() {
+                                @Override
+                                public boolean authenticate(final String username, final String password, final ServerSession session) {
+                                    // All connections are accepted
+                                    return true;
+                                }
+                            }, keyPairProvider);
+
+                    sshWrappers.add(sshServer);
                 } catch (final Exception e) {
                     LOG.warn("Cannot start simulated device on {}, skipping", address, e);
                     // Close local server and continue
@@ -225,10 +255,12 @@ public class NetconfDeviceSimulator implements Closeable {
         return openDevices;
     }
 
-    private char[] getPemArray() {
+    private PEMGeneratorHostKeyProvider getPemGeneratorHostKeyProvider() {
         try {
-            return PEMGenerator.readOrGeneratePK(new File("PK")).toCharArray();
+            final Path tempFile = Files.createTempFile("tempKeyNetconfTest", "suffix");
+            return new PEMGeneratorHostKeyProvider(tempFile.toAbsolutePath().toString());
         } catch (final IOException e) {
+            LOG.error("Unable to generate PEM key", e);
             throw new RuntimeException(e);
         }
     }
@@ -317,10 +349,15 @@ public class NetconfDeviceSimulator implements Closeable {
 
     @Override
     public void close() {
+        for (final SshProxyServer sshWrapper : sshWrappers) {
+            sshWrapper.close();
+        }
         for (final Channel deviceCh : devicesChannels) {
             deviceCh.close();
         }
         nettyThreadgroup.shutdownGracefully();
+        minaTimerExecutor.shutdownNow();
+        nioExecutor.shutdownNow();
         // close Everything
     }
 
index 78efe7e9723154bd218bafa5d248af0329fb567d..3c63204881aa9ea0ef3e0909ad302e73187d5b31 100644 (file)
@@ -37,6 +37,8 @@ import org.xml.sax.SAXException;
 
 public final class XmlElement {
 
+    public static final String DEFAULT_NAMESPACE_PREFIX = "";
+
     private final Element element;
     private static final Logger logger = LoggerFactory.getLogger(XmlElement.class);
 
@@ -72,16 +74,16 @@ public final class XmlElement {
         return xmlElement;
     }
 
-    private static Map<String, String> extractNamespaces(Element typeElement) throws NetconfDocumentedException {
+    private Map<String, String> extractNamespaces() throws NetconfDocumentedException {
         Map<String, String> namespaces = new HashMap<>();
-        NamedNodeMap attributes = typeElement.getAttributes();
+        NamedNodeMap attributes = element.getAttributes();
         for (int i = 0; i < attributes.getLength(); i++) {
             Node attribute = attributes.item(i);
             String attribKey = attribute.getNodeName();
             if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
                 String prefix;
                 if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
-                    prefix = "";
+                    prefix = DEFAULT_NAMESPACE_PREFIX;
                 } else {
                     if (!attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":")){
                         throw new NetconfDocumentedException("Attribute doesn't start with :",
@@ -94,6 +96,15 @@ public final class XmlElement {
                 namespaces.put(prefix, attribute.getNodeValue());
             }
         }
+
+        // namespace does not have to be defined on this element but inherited
+        if(!namespaces.containsKey(DEFAULT_NAMESPACE_PREFIX)) {
+            Optional<String> namespaceOptionally = getNamespaceOptionally();
+            if(namespaceOptionally.isPresent()) {
+                namespaces.put(DEFAULT_NAMESPACE_PREFIX, namespaceOptionally.get());
+            }
+        }
+
         return namespaces;
     }
 
@@ -132,7 +143,7 @@ public final class XmlElement {
     }
 
     public String getName() {
-        if (element.getLocalName()!=null && !element.getLocalName().equals("")){
+        if (element.getLocalName()!=null && !element.getLocalName().equals(DEFAULT_NAMESPACE_PREFIX)){
             return element.getLocalName();
         }
         return element.getTagName();
@@ -327,7 +338,7 @@ public final class XmlElement {
     public String getTextContent() throws NetconfDocumentedException {
         NodeList childNodes = element.getChildNodes();
         if (childNodes.getLength() == 0) {
-            return "";
+            return DEFAULT_NAMESPACE_PREFIX;
         }
         for(int i = 0; i < childNodes.getLength(); i++) {
             Node textChild = childNodes.item(i);
@@ -356,7 +367,7 @@ public final class XmlElement {
 
     public String getNamespaceAttribute() throws MissingNameSpaceException {
         String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
-        if (attribute == null || attribute.equals("")){
+        if (attribute == null || attribute.equals(DEFAULT_NAMESPACE_PREFIX)){
             throw new MissingNameSpaceException(String.format("Element %s must specify namespace",
                     toString()),
                     NetconfDocumentedException.ErrorType.application,
@@ -415,14 +426,14 @@ public final class XmlElement {
      * is found value will be null.
      */
     public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() throws NetconfDocumentedException {
-        Map<String, String> namespaces = extractNamespaces(element);
+        Map<String, String> namespaces = extractNamespaces();
         String textContent = getTextContent();
         int indexOfColon = textContent.indexOf(':');
         String prefix;
         if (indexOfColon > -1) {
             prefix = textContent.substring(0, indexOfColon);
         } else {
-            prefix = "";
+            prefix = DEFAULT_NAMESPACE_PREFIX;
         }
         if (!namespaces.containsKey(prefix)) {
             throw new IllegalArgumentException("Cannot find namespace for " + XmlUtil.toString(element) + ". Prefix from content is "
index 361f4cfa0866596233f7101b1eb09b13e126fde1..b392c5b6726ea2e3ed3c8fc63f943a9bed75dacc 100644 (file)
@@ -38,6 +38,8 @@
     <module>netconf-auth</module>
     <module>netconf-usermanager</module>
     <module>netconf-testtool</module>
+
+    <module>netconf-artifacts</module>
   </modules>
 
   <dependencies>
diff --git a/pom.xml b/pom.xml
index bb8ad1dbeba6459a1b9e5acb90070febc1b5a72a..53ef5601bb357de5ed1c8061a9433ef9211e4769 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -53,8 +53,7 @@
     <!-- <module>third-party/net.sf.jung2</module> -->
     <!-- <module>third-party/jersey-servlet</module> -->
     <!-- <module>third-party/org.apache.catalina.filters.CorsFilter</module> -->
-    <module>third-party/ganymed</module>
-
+    
     <module>third-party/commons/thirdparty</module>
 
     <!-- SAL bundles -->
diff --git a/third-party/ganymed/pom.xml b/third-party/ganymed/pom.xml
deleted file mode 100644 (file)
index 676e2a2..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>commons.thirdparty</artifactId>
-        <version>1.2.0-SNAPSHOT</version>
-        <relativePath>../commons/thirdparty</relativePath>
-    </parent>
-
-    <groupId>org.opendaylight.controller.thirdparty</groupId>
-    <artifactId>ganymed</artifactId>
-    <version>1.2.0-SNAPSHOT</version>
-    <packaging>bundle</packaging>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>5.0.0</version>
-        </dependency>
-        <dependency>
-            <groupId>ch.ethz.ganymed</groupId>
-            <artifactId>ganymed-ssh2</artifactId>
-            <version>261</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <Export-Package>ch.ethz.ssh2.*</Export-Package>
-                        <Embed-Dependency>ganymed-ssh2;scope=compile</Embed-Dependency>
-                        <Embed-Transitive>true</Embed-Transitive>
-                    </instructions>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-enforcer-plugin</artifactId>
-                <version>${enforcer.version}</version>
-                <executions>
-                    <execution>
-                        <id>enforce-no-snapshots</id>
-                        <goals>
-                            <goal>enforce</goal>
-                        </goals>
-                        <configuration>
-                            <rules>
-                                <bannedDependencies>
-                                    <excludes>
-                                        <exclude>ch.ethz.ganymed:ganymed-ssh2:*</exclude>
-                                    </excludes>
-                                    <includes>
-                                        <include>ch.ethz.ganymed:ganymed-ssh2:[261]</include>
-                                    </includes>
-                                </bannedDependencies>
-                            </rules>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-</project>
-
-
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java
deleted file mode 100644 (file)
index aa13c40..0000000
+++ /dev/null
@@ -1,1424 +0,0 @@
-/*
- * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2;
-
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.net.Socket;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketTimeoutException;
-import java.security.SecureRandom;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.auth.AuthenticationManager;
-import ch.ethz.ssh2.channel.ChannelManager;
-import ch.ethz.ssh2.crypto.CryptoWishList;
-import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
-import ch.ethz.ssh2.crypto.digest.MAC;
-import ch.ethz.ssh2.packets.PacketIgnore;
-import ch.ethz.ssh2.transport.KexManager;
-import ch.ethz.ssh2.transport.TransportManager;
-import ch.ethz.ssh2.util.TimeoutService;
-import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
-
-/**
- * A <code>Connection</code> is used to establish an encrypted TCP/IP
- * connection to a SSH-2 server.
- * <p>
- * Typically, one
- * <ol>
- * <li>creates a {@link #Connection(String) Connection} object.</li>
- * <li>calls the {@link #connect() connect()} method.</li>
- * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
- * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
- * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
- * </ol>
- *
- * @author Christian Plattner
- * @version $Id: Connection.java 69 2013-08-09 06:39:56Z dkocher@sudo.ch $
- */
-
-public class Connection
-{
-    /**
-     * The identifier presented to the SSH-2 server. This is the same
-     * as the "softwareversion" defined in RFC 4253.
-     * <p/>
-     * <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
-     * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
-     */
-    private String softwareversion = String.format("Ganymed_%s", Version.getSpecification());
-
-       /* Will be used to generate all random data needed for the current connection.
-        * Note: SecureRandom.nextBytes() is thread safe.
-        */
-
-    private SecureRandom generator;
-
-    private Socket precreatedSocket;
-
-    public Connection(Socket socket) {
-        this.precreatedSocket = socket;
-        this.hostname = socket.getInetAddress().getHostName();
-        this.port = socket.getPort();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported cipher algorithms by this implementation.
-     */
-    public static synchronized String[] getAvailableCiphers()
-    {
-        return BlockCipherFactory.getDefaultCipherList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported MAC algorthims by this implementation.
-     */
-    public static synchronized String[] getAvailableMACs()
-    {
-        return MAC.getMacList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported server host key algorthims by this implementation.
-     */
-    public static synchronized String[] getAvailableServerHostKeyAlgorithms()
-    {
-        return KexManager.getDefaultServerHostkeyAlgorithmList();
-    }
-
-    private AuthenticationManager am;
-
-    private boolean authenticated = false;
-    private ChannelManager cm;
-
-    private CryptoWishList cryptoWishList = new CryptoWishList();
-
-    private DHGexParameters dhgexpara = new DHGexParameters();
-
-    private final String hostname;
-
-    private final int port;
-
-    private TransportManager tm;
-
-    private boolean tcpNoDelay = false;
-
-    private ProxyData proxyData = null;
-
-    private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     * <p>
-     * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
-     *
-     * @param hostname the hostname of the SSH-2 server.
-     */
-    public Connection(String hostname)
-    {
-        this(hostname, 22);
-    }
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     *
-     * @param hostname
-     *            the host where we later want to connect to.
-     * @param port
-     *            port on the server, normally 22.
-     */
-    public Connection(String hostname, int port)
-    {
-        this.hostname = hostname;
-        this.port = port;
-    }
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     *
-     * @param hostname
-     *            the host where we later want to connect to.
-     * @param port
-     *            port on the server, normally 22.
-     * @param softwareversion
-     *                         Allows you to set a custom "softwareversion" string as defined in RFC 4253.
-     *                         <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
-     *          US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
-     */
-    public Connection(String hostname, int port, String softwareversion)
-    {
-        this.hostname = hostname;
-        this.port = port;
-        this.softwareversion = softwareversion;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on DSA (it uses DSA to sign a challenge sent by the server).
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pem
-     *            A <code>String</code> containing the DSA private key of the
-     *            user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
-     *            linefeeds.
-     * @param password
-     *            If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
-     *            must specify the password. Otherwise, this argument will be
-     *            ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     *
-     * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
-     *                       methods, this method is just a wrapper for it and will
-     *            disappear in future builds.
-     *
-     */
-    public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pem == null)
-            throw new IllegalArgumentException("pem argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
-
-        return authenticated;
-    }
-
-    /**
-     * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
-     * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
-            throws IOException
-    {
-        return authenticateWithKeyboardInteractive(user, null, cb);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on "keyboard-interactive", specified in
-     * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
-     * callback object which will be feeded with challenges generated by the
-     * server. Answers are then sent back to the server. It is possible that the
-     * callback will be called several times during the invocation of this
-     * method (e.g., if the server replies to the callback's answer(s) with
-     * another challenge...)
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: some SSH servers advertise "keyboard-interactive", however, any
-     * interactive request will be denied (without having sent any challenge to
-     * the client).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param submethods
-     *            An array of submethod names, see
-     *            draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
-     *            to indicate an empty list.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
-                                                                    InteractiveCallback cb) throws IOException
-    {
-        if (cb == null)
-            throw new IllegalArgumentException("Callback may not ne NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        authenticated = am.authenticateInteractive(user, submethods, cb);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * sends username and password to the server.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: if this method fails, then please double-check that it is actually
-     * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
-     * <p>
-     * Often, password authentication is disabled, but users are not aware of it.
-     * Many servers only offer "publickey" and "keyboard-interactive". However,
-     * even though "keyboard-interactive" *feels* like password authentication
-     * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
-     *
-     * @param user
-     * @param password
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (password == null)
-            throw new IllegalArgumentException("password argument is null");
-
-        authenticated = am.authenticatePassword(user, password);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself.
-     * This method can be used to explicitly use the special "none"
-     * authentication method (where only a username has to be specified).
-     * <p>
-     * Note 1: The "none" method may always be tried by clients, however as by
-     * the specs, the server will not explicitly announce it. In other words,
-     * the "none" token will never show up in the list returned by
-     * {@link #getRemainingAuthMethods(String)}.
-     * <p>
-     * Note 2: no matter which one of the authenticateWithXXX() methods
-     * you call, the library will always issue exactly one initial "none"
-     * authentication request to retrieve the initially allowed list of
-     * authentication methods by the server. Please read RFC 4252 for the
-     * details.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If further authentication steps are needed, <code>false</code>
-     * is returned and one can retry by any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithNone(String user) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-               /* Trigger the sending of the PacketUserauthRequestNone packet */
-               /* (if not already done)                                       */
-
-        authenticated = am.authenticateNone(user);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself.
-     * The authentication method "publickey" works by signing a challenge
-     * sent by the server. The signature is either DSA or RSA based - it
-     * just depends on the type of private key you specify, either a DSA
-     * or RSA private key in PEM format. And yes, this is may seem to be a
-     * little confusing, the method is called "publickey" in the SSH-2 protocol
-     * specification, however since we need to generate a signature, you
-     * actually have to supply a private key =).
-     * <p>
-     * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
-     * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
-     * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
-     * it is not in the expected format. You have to convert it to the OpenSSH
-     * key format by using the "puttygen" tool (can be downloaded from the Putty
-     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
-     * functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemPrivateKey
-     *            A <code>char[]</code> containing a DSA or RSA private key of the
-     *            user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
-     *            tag). The char array may contain linebreaks/linefeeds.
-     * @param password
-     *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
-     *            you must specify a password. Otherwise, this argument will be ignored
-     *            and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
-            throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pemPrivateKey == null)
-            throw new IllegalArgumentException("pemPrivateKey argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
-
-        return authenticated;
-    }
-
-    /**
-     * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
-     * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
-     * it is not in the expected format. You have to convert it to the OpenSSH
-     * key format by using the "puttygen" tool (can be downloaded from the Putty
-     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
-     * functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemFile
-     *            A <code>File</code> object pointing to a file containing a DSA or RSA
-     *            private key of the user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
-     *            tag).
-     * @param password
-     *            If the PEM file is encrypted then you must specify the password.
-     *            Otherwise, this argument will be ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
-            throws IOException
-    {
-        if (pemFile == null)
-            throw new IllegalArgumentException("pemFile argument is null");
-
-        char[] buff = new char[256];
-
-        CharArrayWriter cw = new CharArrayWriter();
-
-        FileReader fr = new FileReader(pemFile);
-
-        while (true)
-        {
-            int len = fr.read(buff);
-            if (len < 0)
-                break;
-            cw.write(buff, 0, len);
-        }
-
-        fr.close();
-
-        return authenticateWithPublicKey(user, cw.toCharArray(), password);
-    }
-
-    /**
-     * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
-     * but it is best to add connection monitors before invoking
-     * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
-     * a successful connect(), but the connection has died in the mean time. Then,
-     * your connection monitor won't be notified.)
-     * <p>
-     * You can add as many monitors as you like. If a monitor has already been added, then
-     * this method does nothing.
-     *
-     * @see ConnectionMonitor
-     *
-     * @param cmon An object implementing the {@link ConnectionMonitor} interface.
-     */
-    public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
-    {
-        if (cmon == null)
-            throw new IllegalArgumentException("cmon argument is null");
-
-        if (!connectionMonitors.contains(cmon))
-        {
-            connectionMonitors.add(cmon);
-
-            if (tm != null)
-                tm.setConnectionMonitors(connectionMonitors);
-        }
-    }
-
-    /**
-     * Remove a {@link ConnectionMonitor} from this connection.
-     *
-     * @param cmon
-     * @return whether the monitor could be removed
-     */
-    public synchronized boolean removeConnectionMonitor(ConnectionMonitor cmon)
-    {
-        if (cmon == null)
-            throw new IllegalArgumentException("cmon argument is null");
-
-        boolean existed = connectionMonitors.remove(cmon);
-
-        if (tm != null)
-            tm.setConnectionMonitors(connectionMonitors);
-
-        return existed;
-    }
-
-    /**
-     * Close the connection to the SSH-2 server. All assigned sessions will be
-     * closed, too. Can be called at any time. Don't forget to call this once
-     * you don't need a connection anymore - otherwise the receiver thread may
-     * run forever.
-     */
-    public synchronized void close()
-    {
-        Throwable t = new Throwable("Closed due to user request.");
-        close(t, false);
-    }
-
-    public synchronized void close(Throwable t, boolean hard)
-    {
-        if (cm != null)
-            cm.closeAllChannels();
-
-        if (tm != null)
-        {
-            tm.close(t, hard == false);
-            tm = null;
-        }
-        am = null;
-        cm = null;
-        authenticated = false;
-    }
-
-    /**
-     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
-     *
-     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
-     * @throws IOException
-     */
-    public synchronized ConnectionInfo connect() throws IOException
-    {
-        return connect(null, 0, 0);
-    }
-
-    /**
-     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
-     *
-     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
-     * @throws IOException
-     */
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
-    {
-        return connect(verifier, 0, 0);
-    }
-
-    /**
-     * Connect to the SSH-2 server and, as soon as the server has presented its
-     * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
-     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
-     * method of the <code>verifier</code> to ask for permission to proceed.
-     * If <code>verifier</code> is <code>null</code>, then any host key will be
-     * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
-     * VERY easy (somebody could put a proxy SSH server between you and the real server).
-     * <p>
-     * Note: The verifier will be called before doing any crypto calculations
-     * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
-     * no CPU cycles are wasted (and the evil server has less information about us).
-     * <p>
-     * However, it is still possible that the server presented a fake host key: the server
-     * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
-     * a signature that matches its host key. Don't worry, the library will detect such
-     * a scenario later when checking the signature (the signature cannot be checked before
-     * having completed the diffie-hellman exchange).
-     * <p>
-     * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
-     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
-     * will *NOT* be called from the current thread, the call is being made from a
-     * background thread (there is a background dispatcher thread for every
-     * established connection).
-     * <p>
-     * Note 3: This method will block as long as the key exchange of the underlying connection
-     * has not been completed (and you have not specified any timeouts).
-     * <p>
-     * Note 4: If you want to re-use a connection object that was successfully connected,
-     * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
-     *
-     * @param verifier
-     *            An object that implements the
-     *            {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
-     *            to accept any server host key - NOT recommended.
-     *
-     * @param connectTimeout
-     *            Connect the underlying TCP socket to the server with the given timeout
-     *            value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
-     *            used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
-     *            connection establishment to the proxy.
-     *
-     * @param kexTimeout
-     *            Timeout for complete connection establishment (non-negative,
-     *            in milliseconds). Zero means no timeout. The timeout counts from the
-     *            moment you invoke the connect() method and is cancelled as soon as the
-     *            first key-exchange round has finished. It is possible that
-     *            the timeout event will be fired during the invocation of the
-     *            <code>verifier</code> callback, but it will only have an effect after
-     *            the <code>verifier</code> returns.
-     *
-     * @return A {@link ConnectionInfo} object containing the details of
-     *            the established connection.
-     *
-     * @throws IOException
-     *            If any problem occurs, e.g., the server's host key is not
-     *            accepted by the <code>verifier</code> or there is problem during
-     *            the initial crypto setup (e.g., the signature sent by the server is wrong).
-     *            <p>
-     *            In case of a timeout (either connectTimeout or kexTimeout)
-     *            a SocketTimeoutException is thrown.
-     *            <p>
-     *            An exception may also be thrown if the connection was already successfully
-     *            connected (no matter if the connection broke in the mean time) and you invoke
-     *            <code>connect()</code> again without having called {@link #close()} first.
-     *            <p>
-     *            If a HTTP proxy is being used and the proxy refuses the connection,
-     *            then a {@link HTTPProxyException} may be thrown, which
-     *            contains the details returned by the proxy. If the proxy is buggy and does
-     *            not return a proper HTTP response, then a normal IOException is thrown instead.
-     */
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
-            throws IOException
-    {
-        final class TimeoutState
-        {
-            boolean isCancelled = false;
-            boolean timeoutSocketClosed = false;
-        }
-
-        if (tm != null)
-            throw new IOException("Connection to " + hostname + " is already in connected state!");
-
-        if (connectTimeout < 0)
-            throw new IllegalArgumentException("connectTimeout must be non-negative!");
-
-        if (kexTimeout < 0)
-            throw new IllegalArgumentException("kexTimeout must be non-negative!");
-
-        final TimeoutState state = new TimeoutState();
-
-        tm = new TransportManager();
-        tm.setSoTimeout(connectTimeout);
-        tm.setConnectionMonitors(connectionMonitors);
-
-               /* Make sure that the runnable below will observe the new value of "tm"
-                * and "state" (the runnable will be executed in a different thread, which
-                * may be already running, that is why we need a memory barrier here).
-                * See also the comment in Channel.java if you
-                * are interested in the details.
-                * 
-                * OKOK, this is paranoid since adding the runnable to the todo list
-                * of the TimeoutService will ensure that all writes have been flushed
-                * before the Runnable reads anything
-                * (there is a synchronized block in TimeoutService.addTimeoutHandler).
-                */
-
-        synchronized (tm)
-        {
-                       /* We could actually synchronize on anything. */
-        }
-
-        try
-        {
-            TimeoutToken token = null;
-
-            if (kexTimeout > 0)
-            {
-                final Runnable timeoutHandler = new Runnable()
-                {
-                    public void run()
-                    {
-                        synchronized (state)
-                        {
-                            if (state.isCancelled)
-                                return;
-                            state.timeoutSocketClosed = true;
-                            tm.close(new SocketTimeoutException("The connect timeout expired"), false);
-                        }
-                    }
-                };
-
-                long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
-
-                token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
-            }
-
-            try
-            {
-
-                if (precreatedSocket != null) {
-                    tm.clientInit(precreatedSocket, softwareversion, cryptoWishList, verifier, dhgexpara,
-                            getOrCreateSecureRND());
-                } else {
-                    tm.clientInit(hostname, port, softwareversion, cryptoWishList, verifier, dhgexpara, connectTimeout,
-                            getOrCreateSecureRND(), proxyData);
-                }
-            }
-            catch (SocketTimeoutException se)
-            {
-                throw (SocketTimeoutException) new SocketTimeoutException(
-                        "The connect() operation on the socket timed out.").initCause(se);
-            }
-
-            tm.setTcpNoDelay(tcpNoDelay);
-
-                       /* Wait until first KEX has finished */
-
-            ConnectionInfo ci = tm.getConnectionInfo(1);
-
-                       /* Now try to cancel the timeout, if needed */
-
-            if (token != null)
-            {
-                TimeoutService.cancelTimeoutHandler(token);
-
-                               /* Were we too late? */
-
-                synchronized (state)
-                {
-                    if (state.timeoutSocketClosed)
-                        throw new IOException("This exception will be replaced by the one below =)");
-                                       /* Just in case the "cancelTimeoutHandler" invocation came just a little bit
-                                        * too late but the handler did not enter the semaphore yet - we can
-                                        * still stop it.
-                                        */
-                    state.isCancelled = true;
-                }
-            }
-
-            return ci;
-        }
-        catch (SocketTimeoutException ste)
-        {
-            throw ste;
-        }
-        catch (IOException e1)
-        {
-                       /* This will also invoke any registered connection monitors */
-            close(new Throwable("There was a problem during connect."), false);
-
-            synchronized (state)
-            {
-                               /* Show a clean exception, not something like "the socket is closed!?!" */
-                if (state.timeoutSocketClosed)
-                    throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
-            }
-
-                       /* Do not wrap a HTTPProxyException */
-            if (e1 instanceof HTTPProxyException)
-                throw e1;
-
-            throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
-                    .initCause(e1);
-        }
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}.
-     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
-     * port via the secure tunnel to another host (which may or may not be
-     * identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent forwardings.
-     *
-     * @param local_port the local port the LocalPortForwarder shall bind to.
-     * @param host_to_connect target address (IP or hostname)
-     * @param port_to_connect target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
-                                                                    int port_to_connect) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}.
-     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
-     * port via the secure tunnel to another host (which may or may not be
-     * identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent forwardings.
-     *
-     * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
-     * @param host_to_connect target address (IP or hostname)
-     * @param port_to_connect target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
-                                                                    int port_to_connect) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalStreamForwarder}.
-     * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
-     * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
-     * (which may or may not be identical to the remote SSH-2 server).
-     *
-     * @param host_to_connect
-     * @param port_to_connect
-     * @return A {@link LocalStreamForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
-            throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward, connection is not authenticated.");
-
-        return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Create a very basic {@link SCPClient} that can be used to copy
-     * files from/to the SSH-2 server.
-     * <p>
-     * Works only after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent SCP clients.
-     * <p>
-     * Note: This factory method will probably disappear in the future.
-     *
-     * @return A {@link SCPClient} object.
-     * @throws IOException
-     */
-    public synchronized SCPClient createSCPClient() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
-
-        return new SCPClient(this);
-    }
-
-    /**
-     * Force an asynchronous key re-exchange (the call does not block). The
-     * latest values set for MAC, Cipher and DH group exchange parameters will
-     * be used. If a key exchange is currently in progress, then this method has
-     * the only effect that the so far specified parameters will be used for the
-     * next (server driven) key exchange.
-     * <p>
-     * Note: This implementation will never start a key exchange (other than the initial one)
-     * unless you or the SSH-2 server ask for it.
-     *
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-    public synchronized void forceKeyExchange() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        tm.forceKeyExchange(cryptoWishList, dhgexpara, null, null);
-    }
-
-    /**
-     * Returns the hostname that was passed to the constructor.
-     *
-     * @return the hostname
-     */
-    public synchronized String getHostname()
-    {
-        return hostname;
-    }
-
-    /**
-     * Returns the port that was passed to the constructor.
-     *
-     * @return the TCP port
-     */
-    public synchronized int getPort()
-    {
-        return port;
-    }
-
-    /**
-     * Returns a {@link ConnectionInfo} object containing the details of
-     * the connection. Can be called as soon as the connection has been
-     * established (successfully connected).
-     *
-     * @return A {@link ConnectionInfo} object.
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-    public synchronized ConnectionInfo getConnectionInfo() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException(
-                    "Cannot get details of connection, you need to establish a connection first.");
-        return tm.getConnectionInfo(1);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * can be used to tell which authentication methods are supported by the
-     * server at a certain stage of the authentication process (for the given
-     * username).
-     * <p>
-     * Note 1: the username will only be used if no authentication step was done
-     * so far (it will be used to ask the server for a list of possible
-     * authentication methods by sending the initial "none" request). Otherwise,
-     * this method ignores the user name and returns a cached method list
-     * (which is based on the information contained in the last negative server response).
-     * <p>
-     * Note 2: the server may return method names that are not supported by this
-     * implementation.
-     * <p>
-     * After a successful authentication, this method must not be called
-     * anymore.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     *
-     * @return a (possibly emtpy) array holding authentication method names.
-     * @throws IOException
-     */
-    public synchronized String[] getRemainingAuthMethods(String user) throws IOException
-    {
-        if (user == null)
-            throw new IllegalArgumentException("user argument may not be NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        return am.getRemainingMethods(user);
-    }
-
-    /**
-     * Determines if the authentication phase is complete. Can be called at any
-     * time.
-     *
-     * @return <code>true</code> if no further authentication steps are
-     *         needed.
-     */
-    public synchronized boolean isAuthenticationComplete()
-    {
-        return authenticated;
-    }
-
-    /**
-     * Returns true if there was at least one failed authentication request and
-     * the last failed authentication request was marked with "partial success"
-     * by the server. This is only needed in the rare case of SSH-2 server setups
-     * that cannot be satisfied with a single successful authentication request
-     * (i.e., multiple authentication steps are needed.)
-     * <p>
-     * If you are interested in the details, then have a look at RFC4252.
-     *
-     * @return if the there was a failed authentication step and the last one
-     *         was marked as a "partial success".
-     */
-    public synchronized boolean isAuthenticationPartialSuccess()
-    {
-        if (am == null)
-            return false;
-
-        return am.getPartialSuccess();
-    }
-
-    /**
-     * Checks if a specified authentication method is available. This method is
-     * actually just a wrapper for {@link #getRemainingAuthMethods(String)
-     * getRemainingAuthMethods()}.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param method
-     *            An authentication method name (e.g., "publickey", "password",
-     *            "keyboard-interactive") as specified by the SSH-2 standard.
-     * @return if the specified authentication method is currently available.
-     * @throws IOException
-     */
-    public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
-    {
-        if (method == null)
-            throw new IllegalArgumentException("method argument may not be NULL!");
-
-        String methods[] = getRemainingAuthMethods(user);
-
-        for (int i = 0; i < methods.length; i++)
-        {
-            if (methods[i].compareTo(method) == 0)
-                return true;
-        }
-
-        return false;
-    }
-
-    private SecureRandom getOrCreateSecureRND()
-    {
-        if (generator == null)
-            generator = new SecureRandom();
-
-        return generator;
-    }
-
-    /**
-     * Open a new {@link Session} on this connection. Works only after one has passed
-     * successfully the authentication step. There is no limit on the number of
-     * concurrent sessions.
-     *
-     * @return A {@link Session} object.
-     * @throws IOException
-     */
-    public synchronized Session openSession() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot open session, connection is not authenticated.");
-
-        return new Session(cm, getOrCreateSecureRND());
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
-     * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-    public synchronized void sendIgnorePacket() throws IOException
-    {
-        SecureRandom rnd = getOrCreateSecureRND();
-
-        byte[] data = new byte[rnd.nextInt(16)];
-        rnd.nextBytes(data);
-
-        sendIgnorePacket(data);
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet with the given data attribute.
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-    public synchronized void sendIgnorePacket(byte[] data) throws IOException
-    {
-        if (data == null)
-            throw new IllegalArgumentException("data argument must not be null.");
-
-        if (tm == null)
-            throw new IllegalStateException(
-                    "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
-
-        PacketIgnore pi = new PacketIgnore();
-        pi.setData(data);
-
-        tm.sendMessage(pi.getPayload());
-    }
-
-    /**
-     * Removes duplicates from a String array, keeps only first occurence
-     * of each element. Does not destroy order of elements; can handle nulls.
-     * Uses a very efficient O(N^2) algorithm =)
-     *
-     * @param list a String array.
-     * @return a cleaned String array.
-     */
-    private String[] removeDuplicates(String[] list)
-    {
-        if ((list == null) || (list.length < 2))
-            return list;
-
-        String[] list2 = new String[list.length];
-
-        int count = 0;
-
-        for (int i = 0; i < list.length; i++)
-        {
-            boolean duplicate = false;
-
-            String element = list[i];
-
-            for (int j = 0; j < count; j++)
-            {
-                if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
-                {
-                    duplicate = true;
-                    break;
-                }
-            }
-
-            if (duplicate)
-                continue;
-
-            list2[count++] = list[i];
-        }
-
-        if (count == list2.length)
-            return list2;
-
-        String[] tmp = new String[count];
-        System.arraycopy(list2, 0, tmp, 0, count);
-
-        return tmp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-    public synchronized void setClient2ServerCiphers(String[] ciphers)
-    {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.c2s_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-    public synchronized void setClient2ServerMACs(String[] macs)
-    {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.c2s_mac_algos = macs;
-    }
-
-    /**
-     * Sets the parameters for the diffie-hellman group exchange. Unless you
-     * know what you are doing, you will never need this. Default values are
-     * defined in the {@link DHGexParameters} class.
-     *
-     * @param dgp {@link DHGexParameters}, non null.
-     *
-     */
-    public synchronized void setDHGexParameters(DHGexParameters dgp)
-    {
-        if (dgp == null)
-            throw new IllegalArgumentException();
-
-        dhgexpara = dgp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-    public synchronized void setServer2ClientCiphers(String[] ciphers)
-    {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.s2c_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-    public synchronized void setServer2ClientMACs(String[] macs)
-    {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.s2c_mac_algos = macs;
-    }
-
-    /**
-     * Define the set of allowed server host key algorithms to be used for
-     * the following key exchange operations.
-     * <p>
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param algos An array of allowed server host key algorithms.
-     *         SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
-     *         The entries of the array must be ordered after preference, i.e.,
-     *  the entry at index 0 is the most preferred one. You must specify
-     *  at least one entry.
-     */
-    public synchronized void setServerHostKeyAlgorithms(String[] algos)
-    {
-        if ((algos == null) || (algos.length == 0))
-            throw new IllegalArgumentException();
-
-        algos = removeDuplicates(algos);
-        KexManager.checkServerHostkeyAlgorithmsList(algos);
-        cryptoWishList.serverHostKeyAlgorithms = algos;
-    }
-
-    /**
-     * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
-     * <p>
-     * Can be called at any time. If the connection has not yet been established
-     * then the passed value will be stored and set after the socket has been set up.
-     * The default value that will be used is <code>false</code>.
-     *
-     * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
-     * @throws IOException
-     */
-    public synchronized void setTCPNoDelay(boolean enable) throws IOException
-    {
-        tcpNoDelay = enable;
-
-        if (tm != null)
-            tm.setTcpNoDelay(enable);
-    }
-
-    /**
-     * Used to tell the library that the connection shall be established through a proxy server.
-     * It only makes sense to call this method before calling the {@link #connect() connect()}
-     * method.
-     * <p>
-     * At the moment, only HTTP proxies are supported.
-     * <p>
-     * Note: This method can be called any number of times. The {@link #connect() connect()}
-     * method will use the value set in the last preceding invocation of this method.
-     *
-     * @see HTTPProxyData
-     *
-     * @param proxyData Connection information about the proxy. If <code>null</code>, then
-     *                  no proxy will be used (non surprisingly, this is also the default).
-     */
-    public synchronized void setProxyData(ProxyData proxyData)
-    {
-        this.proxyData = proxyData;
-    }
-
-    /**
-     * Request a remote port forwarding.
-     * If successful, then forwarded connections will be redirected to the given target address.
-     * You can cancle a requested remote port forwarding by calling
-     * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
-     * <p>
-     * A call of this method will block until the peer either agreed or disagreed to your request-
-     * <p>
-     * Note 1: this method typically fails if you
-     * <ul>
-     * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
-     * &lt; 1024)</li>
-     * <li>or pass a port number that is already in use on the remote server</li>
-     * <li>or if remote port forwarding is disabled on the server.</li>
-     * </ul>
-     * <p>
-     * Note 2: (from the openssh man page): By default, the listening socket on the server will be
-     * bound to the loopback interface only. This may be overriden by specifying a bind address.
-     * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
-     * is enabled (see sshd_config(5)).
-     *
-     * @param bindAddress address to bind to on the server:
-     *                    <ul>
-     *                    <li>"" means that connections are to be accepted on all protocol families
-     *                    supported by the SSH implementation</li>
-     *                    <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
-     *                    <li>"::" means to listen on all IPv6 addresses</li>
-     *                    <li>"localhost" means to listen on all protocol families supported by the SSH
-     *                    implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
-     *                    <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
-     *                    IPv4 and IPv6 respectively</li>
-     *                    </ul>
-     * @param bindPort port number to bind on the server (must be &gt; 0)
-     * @param targetAddress the target address (IP or hostname)
-     * @param targetPort the target port
-     * @throws IOException
-     */
-    public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
-                                                         int targetPort) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
-            throw new IllegalArgumentException();
-
-        cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
-    }
-
-    /**
-     * Cancel an earlier requested remote port forwarding.
-     * Currently active forwardings will not be affected (e.g., disrupted).
-     * Note that further connection forwarding requests may be received until
-     * this method has returned.
-     *
-     * @param bindPort the allocated port number on the server
-     * @throws IOException if the remote side refuses the cancel request or another low
-     *         level error occurs (e.g., the underlying connection is closed)
-     */
-    public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        cm.requestCancelGlobalForward(bindPort);
-    }
-
-    /**
-     * Provide your own instance of SecureRandom. Can be used, e.g., if you
-     * want to seed the used SecureRandom generator manually.
-     * <p>
-     * The SecureRandom instance is used during key exchanges, public key authentication,
-     * x11 cookie generation and the like.
-     *
-     * @param rnd a SecureRandom instance
-     */
-    public synchronized void setSecureRandom(SecureRandom rnd)
-    {
-        if (rnd == null)
-            throw new IllegalArgumentException();
-
-        this.generator = rnd;
-    }
-}
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java
deleted file mode 100644 (file)
index 8fec9a0..0000000
+++ /dev/null
@@ -1,1845 +0,0 @@
-/*
- * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2.channel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.ChannelCondition;
-import ch.ethz.ssh2.PtySettings;
-import ch.ethz.ssh2.ServerConnectionCallback;
-import ch.ethz.ssh2.ServerSessionCallback;
-import ch.ethz.ssh2.log.Logger;
-import ch.ethz.ssh2.packets.PacketChannelFailure;
-import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
-import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
-import ch.ethz.ssh2.packets.PacketChannelSuccess;
-import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
-import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
-import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
-import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
-import ch.ethz.ssh2.packets.PacketSessionExecCommand;
-import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
-import ch.ethz.ssh2.packets.PacketSessionStartShell;
-import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
-import ch.ethz.ssh2.packets.PacketSessionX11Request;
-import ch.ethz.ssh2.packets.Packets;
-import ch.ethz.ssh2.packets.TypesReader;
-import ch.ethz.ssh2.server.ServerConnectionState;
-import ch.ethz.ssh2.transport.MessageHandler;
-import ch.ethz.ssh2.transport.TransportManager;
-
-/**
- * ChannelManager. Please read the comments in Channel.java.
- * <p/>
- * Besides the crypto part, this is the core of the library.
- *
- * @author Christian Plattner
- * @version $Id: ChannelManager.java 48 2013-08-01 12:22:33Z cleondris@gmail.com $
- */
-public class ChannelManager implements MessageHandler
-{
-    private static final Logger log = Logger.getLogger(ChannelManager.class);
-
-    private final ServerConnectionState server_state;
-    private final TransportManager tm;
-
-    private final HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
-
-    private final List<Channel> channels = new Vector<Channel>();
-    private int nextLocalChannel = 100;
-    private boolean shutdown = false;
-    private int globalSuccessCounter = 0;
-    private int globalFailedCounter = 0;
-
-    private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
-
-    private final List<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
-
-    private boolean listenerThreadsAllowed = true;
-
-    /**
-     * Constructor for client-mode.
-     * @param tm
-     */
-    public ChannelManager(TransportManager tm)
-    {
-        this.server_state = null;
-        this.tm = tm;
-        tm.registerMessageHandler(this, 80, 100);
-    }
-
-    /**
-     * Constructor for server-mode.
-     * @param state
-     */
-    public ChannelManager(ServerConnectionState state)
-    {
-        this.server_state = state;
-        this.tm = state.tm;
-        tm.registerMessageHandler(this, 80, 100);
-    }
-
-    private Channel getChannel(int id)
-    {
-        synchronized (channels)
-        {
-            for (Channel c : channels)
-            {
-                if (c.localID == id)
-                    return c;
-            }
-        }
-        return null;
-    }
-
-    private void removeChannel(int id)
-    {
-        synchronized (channels)
-        {
-            for (Channel c : channels)
-            {
-                if (c.localID == id)
-                {
-                    channels.remove(c);
-                    break;
-                }
-            }
-        }
-    }
-
-    private int addChannel(Channel c)
-    {
-        synchronized (channels)
-        {
-            channels.add(c);
-            return nextLocalChannel++;
-        }
-    }
-
-    private void waitUntilChannelOpen(Channel c) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        synchronized (c)
-        {
-            while (c.state == Channel.STATE_OPENING)
-            {
-                try
-                {
-                    c.wait();
-                }
-                catch (InterruptedException ignore)
-                {
-                    wasInterrupted = true;
-                }
-            }
-
-            if (c.state != Channel.STATE_OPEN)
-            {
-                removeChannel(c.localID);
-
-                String detail = c.getReasonClosed();
-
-                if (detail == null)
-                    detail = "state: " + c.state;
-
-                throw new IOException("Could not open channel (" + detail + ")");
-            }
-        }
-
-        if (wasInterrupted)
-            Thread.currentThread().interrupt();
-    }
-
-    private void waitForGlobalSuccessOrFailure() throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (channels)
-            {
-                while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
-                {
-                    if (shutdown)
-                    {
-                        throw new IOException("The connection is being shutdown");
-                    }
-
-                    try
-                    {
-                        channels.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                if (globalFailedCounter != 0)
-                {
-                    throw new IOException("The server denied the request (did you enable port forwarding?)");
-                }
-
-                if (globalSuccessCounter == 0)
-                {
-                    throw new IOException("Illegal state.");
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    private void waitForChannelSuccessOrFailure(Channel c) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (c)
-            {
-                while ((c.successCounter == 0) && (c.failedCounter == 0))
-                {
-                    if (c.state != Channel.STATE_OPEN)
-                    {
-                        String detail = c.getReasonClosed();
-
-                        if (detail == null)
-                            detail = "state: " + c.state;
-
-                        throw new IOException("This SSH2 channel is not open (" + detail + ")");
-                    }
-
-                    try
-                    {
-                        c.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                if (c.failedCounter != 0)
-                {
-                    throw new IOException("The server denied the request.");
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
-    {
-        synchronized (x11_magic_cookies)
-        {
-            x11_magic_cookies.put(hexFakeCookie, data);
-        }
-    }
-
-    public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
-    {
-        if (hexFakeCookie == null)
-            throw new IllegalStateException("hexFakeCookie may not be null");
-
-        synchronized (x11_magic_cookies)
-        {
-            x11_magic_cookies.remove(hexFakeCookie);
-        }
-
-        if (killChannels == false)
-            return;
-
-        log.debug("Closing all X11 channels for the given fake cookie");
-
-        List<Channel> channel_copy = new Vector<Channel>();
-
-        synchronized (channels)
-        {
-            channel_copy.addAll(channels);
-        }
-
-        for (Channel c : channel_copy)
-        {
-            synchronized (c)
-            {
-                if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
-                    continue;
-            }
-
-            try
-            {
-                closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
-    }
-
-    public X11ServerData checkX11Cookie(String hexFakeCookie)
-    {
-        synchronized (x11_magic_cookies)
-        {
-            if (hexFakeCookie != null)
-                return x11_magic_cookies.get(hexFakeCookie);
-        }
-        return null;
-    }
-
-    public void closeAllChannels()
-    {
-        log.debug("Closing all channels");
-
-        List<Channel> channel_copy = new Vector<Channel>();
-
-        synchronized (channels)
-        {
-            channel_copy.addAll(channels);
-        }
-
-        for (Channel c : channel_copy)
-        {
-            try
-            {
-                closeChannel(c, "Closing all channels", true);
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
-    }
-
-    public void closeChannel(Channel c, String reason, boolean force) throws IOException
-    {
-        byte msg[] = new byte[5];
-
-        synchronized (c)
-        {
-            if (force)
-            {
-                c.state = Channel.STATE_CLOSED;
-                c.EOF = true;
-            }
-
-            c.setReasonClosed(reason);
-
-            msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
-            msg[1] = (byte) (c.remoteID >> 24);
-            msg[2] = (byte) (c.remoteID >> 16);
-            msg[3] = (byte) (c.remoteID >> 8);
-            msg[4] = (byte) (c.remoteID);
-
-            c.notifyAll();
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(msg);
-            c.closeMessageSent = true;
-        }
-
-        log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
-    }
-
-    public void sendEOF(Channel c) throws IOException
-    {
-        byte[] msg = new byte[5];
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                return;
-
-            msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
-            msg[1] = (byte) (c.remoteID >> 24);
-            msg[2] = (byte) (c.remoteID >> 16);
-            msg[3] = (byte) (c.remoteID >> 8);
-            msg[4] = (byte) (c.remoteID);
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(msg);
-        }
-
-
-        log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
-    }
-
-    public void sendOpenConfirmation(Channel c) throws IOException
-    {
-        PacketChannelOpenConfirmation pcoc = null;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPENING)
-                return;
-
-            c.state = Channel.STATE_OPEN;
-
-            pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(pcoc.getPayload());
-        }
-    }
-
-    public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            while (len > 0)
-            {
-                int thislen = 0;
-                byte[] msg;
-
-                synchronized (c)
-                {
-                    while (true)
-                    {
-                        if (c.state == Channel.STATE_CLOSED)
-                            throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-                        if (c.state != Channel.STATE_OPEN)
-                            throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
-
-                        if (c.remoteWindow != 0)
-                            break;
-
-                        try
-                        {
-                            c.wait();
-                        }
-                        catch (InterruptedException ignore)
-                        {
-                            wasInterrupted = true;
-                        }
-                    }
-
-                    /* len > 0, no sign extension can happen when comparing */
-
-                    thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
-
-                    int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
-
-                    /* The worst case scenario =) a true bottleneck */
-
-                    if (estimatedMaxDataLen <= 0)
-                    {
-                        estimatedMaxDataLen = 1;
-                    }
-
-                    if (thislen > estimatedMaxDataLen)
-                        thislen = estimatedMaxDataLen;
-
-                    c.remoteWindow -= thislen;
-
-                    msg = new byte[1 + 8 + thislen];
-
-                    msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
-                    msg[1] = (byte) (c.remoteID >> 24);
-                    msg[2] = (byte) (c.remoteID >> 16);
-                    msg[3] = (byte) (c.remoteID >> 8);
-                    msg[4] = (byte) (c.remoteID);
-                    msg[5] = (byte) (thislen >> 24);
-                    msg[6] = (byte) (thislen >> 16);
-                    msg[7] = (byte) (thislen >> 8);
-                    msg[8] = (byte) (thislen);
-
-                    System.arraycopy(buffer, pos, msg, 9, thislen);
-                }
-
-                synchronized (c.channelSendLock)
-                {
-                    if (c.closeMessageSent == true)
-                        throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-                    tm.sendMessage(msg);
-                }
-
-                pos += thislen;
-                len -= thislen;
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
-            throws IOException
-    {
-        RemoteForwardingData rfd = new RemoteForwardingData();
-
-        rfd.bindAddress = bindAddress;
-        rfd.bindPort = bindPort;
-        rfd.targetAddress = targetAddress;
-        rfd.targetPort = targetPort;
-
-        synchronized (remoteForwardings)
-        {
-            Integer key = new Integer(bindPort);
-
-            if (remoteForwardings.get(key) != null)
-            {
-                throw new IOException("There is already a forwarding for remote port " + bindPort);
-            }
-
-            remoteForwardings.put(key, rfd);
-        }
-
-        synchronized (channels)
-        {
-            globalSuccessCounter = globalFailedCounter = 0;
-        }
-
-        PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
-        tm.sendMessage(pgf.getPayload());
-
-        log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
-
-        try
-        {
-            waitForGlobalSuccessOrFailure();
-        }
-        catch (IOException e)
-        {
-            synchronized (remoteForwardings)
-            {
-                remoteForwardings.remove(rfd);
-            }
-            throw e;
-        }
-
-        return bindPort;
-    }
-
-    public void requestCancelGlobalForward(int bindPort) throws IOException
-    {
-        RemoteForwardingData rfd = null;
-
-        synchronized (remoteForwardings)
-        {
-            rfd = remoteForwardings.get(new Integer(bindPort));
-
-            if (rfd == null)
-                throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
-        }
-
-        synchronized (channels)
-        {
-            globalSuccessCounter = globalFailedCounter = 0;
-        }
-
-        PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
-                rfd.bindPort);
-        tm.sendMessage(pgcf.getPayload());
-
-        log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
-
-        waitForGlobalSuccessOrFailure();
-
-        /* Only now we are sure that no more forwarded connections will arrive */
-
-        synchronized (remoteForwardings)
-        {
-            remoteForwardings.remove(rfd);
-        }
-    }
-
-    public void registerThread(IChannelWorkerThread thr) throws IOException
-    {
-        synchronized (listenerThreads)
-        {
-            if (listenerThreadsAllowed == false)
-                throw new IOException("Too late, this connection is closed.");
-            listenerThreads.add(thr);
-        }
-    }
-
-    public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
-                                          int originator_port) throws IOException
-    {
-        Channel c = new Channel(this);
-
-        synchronized (c)
-        {
-            c.localID = addChannel(c);
-            // end of synchronized block forces writing out to main memory
-        }
-
-        PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
-                c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
-
-        tm.sendMessage(dtc.getPayload());
-
-        waitUntilChannelOpen(c);
-
-        return c;
-    }
-
-    public Channel openSessionChannel() throws IOException
-    {
-        Channel c = new Channel(this);
-
-        synchronized (c)
-        {
-            c.localID = addChannel(c);
-            // end of synchronized block forces the writing out to main memory
-        }
-
-        log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
-
-        PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
-        tm.sendMessage(smo.getPayload());
-
-        waitUntilChannelOpen(c);
-
-        return c;
-    }
-
-    public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
-                           int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
-    {
-        PacketSessionPtyRequest spr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-
-            spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
-                    term_width_pixels, term_height_pixels, terminal_modes);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(spr.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("PTY request failed").initCause(e);
-        }
-    }
-
-    public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
-                           String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
-    {
-        PacketSessionX11Request psr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-
-            psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
-                    x11AuthenticationCookie, x11ScreenNumber);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(psr.getPayload());
-        }
-
-        log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The X11 request failed.").initCause(e);
-        }
-    }
-
-    public void requestSubSystem(Channel c, String subSystemName) throws IOException
-    {
-        PacketSessionSubsystemRequest ssr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-
-            ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(ssr.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The subsystem request failed.").initCause(e);
-        }
-    }
-
-    public void requestExecCommand(Channel c, String cmd) throws IOException
-    {
-        this.requestExecCommand(c, cmd, null);
-    }
-
-    /**
-     * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
-     */
-    public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException
-    {
-        PacketSessionExecCommand sm;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-
-            sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(sm.getPayload(charsetName));
-        }
-
-        log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The execute request failed.").initCause(e);
-        }
-    }
-
-    public void requestShell(Channel c) throws IOException
-    {
-        PacketSessionStartShell sm;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-
-            sm = new PacketSessionStartShell(c.remoteID, true);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(sm.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The shell request failed.").initCause(e);
-        }
-    }
-
-    public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen <= 13)
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-        int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
-
-        if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
-
-        if (len != (msglen - 13))
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
-                    + ", got " + len + ")");
-
-        log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
-
-        synchronized (c)
-        {
-            if (c.state == Channel.STATE_CLOSED)
-                return; // ignore
-
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
-                        + c.state + ")");
-
-            if (c.localWindow < len)
-                throw new IOException("Remote sent too much data, does not fit into window.");
-
-            c.localWindow -= len;
-
-            System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
-            c.stderrWritepos += len;
-
-            c.notifyAll();
-        }
-    }
-
-    /**
-     * Wait until for a condition.
-     *
-     * @param c Channel
-     * @param timeout in ms, 0 means no timeout.
-     * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
-     * @return all current events
-     */
-    public int waitForCondition(Channel c, long timeout, int condition_mask)
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            long end_time = 0;
-            boolean end_time_set = false;
-
-            synchronized (c)
-            {
-                while (true)
-                {
-                    int current_cond = 0;
-
-                    int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
-                    int stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
-                    if (stdoutAvail > 0)
-                        current_cond = current_cond | ChannelCondition.STDOUT_DATA;
-
-                    if (stderrAvail > 0)
-                        current_cond = current_cond | ChannelCondition.STDERR_DATA;
-
-                    if (c.EOF)
-                        current_cond = current_cond | ChannelCondition.EOF;
-
-                    if (c.getExitStatus() != null)
-                        current_cond = current_cond | ChannelCondition.EXIT_STATUS;
-
-                    if (c.getExitSignal() != null)
-                        current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
-
-                    if (c.state == Channel.STATE_CLOSED)
-                        return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
-
-                    if ((current_cond & condition_mask) != 0)
-                        return current_cond;
-
-                    if (timeout > 0)
-                    {
-                        if (!end_time_set)
-                        {
-                            end_time = System.currentTimeMillis() + timeout;
-                            end_time_set = true;
-                        }
-                        else
-                        {
-                            timeout = end_time - System.currentTimeMillis();
-
-                            if (timeout <= 0)
-                                return current_cond | ChannelCondition.TIMEOUT;
-                        }
-                    }
-
-                    try
-                    {
-                        if (timeout > 0)
-                            c.wait(timeout);
-                        else
-                            c.wait();
-                    }
-                    catch (InterruptedException e)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public int getAvailable(Channel c, boolean extended) throws IOException
-    {
-        synchronized (c)
-        {
-            int avail;
-
-            if (extended)
-                avail = c.stderrWritepos - c.stderrReadpos;
-            else
-                avail = c.stdoutWritepos - c.stdoutReadpos;
-
-            return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
-        }
-    }
-
-    public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            int copylen = 0;
-            int increment = 0;
-            int remoteID = 0;
-            int localID = 0;
-
-            synchronized (c)
-            {
-                int stdoutAvail = 0;
-                int stderrAvail = 0;
-
-                while (true)
-                {
-                    /*
-                     * Data available? We have to return remaining data even if the
-                     * channel is already closed.
-                     */
-
-                    stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
-                    stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
-                    if ((!extended) && (stdoutAvail != 0))
-                        break;
-
-                    if ((extended) && (stderrAvail != 0))
-                        break;
-
-                    /* Do not wait if more data will never arrive (EOF or CLOSED) */
-
-                    if ((c.EOF) || (c.state != Channel.STATE_OPEN))
-                        return -1;
-
-                    try
-                    {
-                        c.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                /* OK, there is some data. Return it. */
-
-                if (!extended)
-                {
-                    copylen = (stdoutAvail > len) ? len : stdoutAvail;
-                    System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
-                    c.stdoutReadpos += copylen;
-
-                    if (c.stdoutReadpos != c.stdoutWritepos)
-
-                        System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
-                                - c.stdoutReadpos);
-
-                    c.stdoutWritepos -= c.stdoutReadpos;
-                    c.stdoutReadpos = 0;
-                }
-                else
-                {
-                    copylen = (stderrAvail > len) ? len : stderrAvail;
-                    System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
-                    c.stderrReadpos += copylen;
-
-                    if (c.stderrReadpos != c.stderrWritepos)
-
-                        System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
-                                - c.stderrReadpos);
-
-                    c.stderrWritepos -= c.stderrReadpos;
-                    c.stderrReadpos = 0;
-                }
-
-                if (c.state != Channel.STATE_OPEN)
-                    return copylen;
-
-                if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
-                {
-                    int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
-                            Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
-
-                    increment = minFreeSpace - c.localWindow;
-                    c.localWindow = minFreeSpace;
-                }
-
-                remoteID = c.remoteID; /* read while holding the lock */
-                localID = c.localID; /* read while holding the lock */
-            }
-
-            /*
-             * If a consumer reads stdout and stdin in parallel, we may end up with
-             * sending two msgWindowAdjust messages. Luckily, it
-             * does not matter in which order they arrive at the server.
-             */
-
-            if (increment > 0)
-            {
-                log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
-
-                synchronized (c.channelSendLock)
-                {
-                    byte[] msg = c.msgWindowAdjust;
-
-                    msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
-                    msg[1] = (byte) (remoteID >> 24);
-                    msg[2] = (byte) (remoteID >> 16);
-                    msg[3] = (byte) (remoteID >> 8);
-                    msg[4] = (byte) (remoteID);
-                    msg[5] = (byte) (increment >> 24);
-                    msg[6] = (byte) (increment >> 16);
-                    msg[7] = (byte) (increment >> 8);
-                    msg[8] = (byte) (increment);
-
-                    if (c.closeMessageSent == false)
-                        tm.sendMessage(msg);
-                }
-            }
-
-            return copylen;
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-
-    }
-
-    public void msgChannelData(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen <= 9)
-            throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
-
-        if (len != (msglen - 9))
-            throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
-                    + len + ")");
-
-        log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
-
-        synchronized (c)
-        {
-            if (c.state == Channel.STATE_CLOSED)
-                return; // ignore
-
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
-
-            if (c.localWindow < len)
-                throw new IOException("Remote sent too much data, does not fit into window.");
-
-            c.localWindow -= len;
-
-            System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
-            c.stdoutWritepos += len;
-
-            c.notifyAll();
-        }
-    }
-
-    public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 9)
-            throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            final long huge = 0xFFFFffffL; /* 2^32 - 1 */
-
-            c.remoteWindow += (windowChange & huge); /* avoid sign extension */
-
-            /* TODO - is this a good heuristic? */
-
-            if ((c.remoteWindow > huge))
-                c.remoteWindow = huge;
-
-            c.notifyAll();
-        }
-
-
-        log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
-    }
-
-    public void msgChannelOpen(byte[] msg, int msglen) throws IOException
-    {
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        String channelType = tr.readString();
-        int remoteID = tr.readUINT32(); /* sender channel */
-        int remoteWindow = tr.readUINT32(); /* initial window size */
-        int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
-
-        if ("x11".equals(channelType))
-        {
-            synchronized (x11_magic_cookies)
-            {
-                /* If we did not request X11 forwarding, then simply ignore this bogus request. */
-
-                if (x11_magic_cookies.size() == 0)
-                {
-                    PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
-                            Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
-
-                    tm.sendAsynchronousMessage(pcof.getPayload());
-
-                    log.warning("Unexpected X11 request, denying it!");
-
-                    return;
-                }
-            }
-
-            String remoteOriginatorAddress = tr.readString();
-            int remoteOriginatorPort = tr.readUINT32();
-
-            Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-            }
-
-            /*
-             * The open confirmation message will be sent from another thread
-             */
-
-            RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
-            rxat.setDaemon(true);
-            rxat.start();
-
-            return;
-        }
-
-        if ("forwarded-tcpip".equals(channelType))
-        {
-            String remoteConnectedAddress = tr.readString(); /* address that was connected */
-            int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
-            String remoteOriginatorAddress = tr.readString(); /* originator IP address */
-            int remoteOriginatorPort = tr.readUINT32(); /* originator port */
-
-            RemoteForwardingData rfd = null;
-
-            synchronized (remoteForwardings)
-            {
-                rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
-            }
-
-            if (rfd == null)
-            {
-                PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
-                        Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
-                        "No thanks, unknown port in forwarded-tcpip request", "");
-
-                /* Always try to be polite. */
-
-                tm.sendAsynchronousMessage(pcof.getPayload());
-
-                log.debug("Unexpected forwarded-tcpip request, denying it!");
-
-                return;
-            }
-
-            Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-            }
-
-            /*
-             * The open confirmation message will be sent from another thread.
-             */
-
-            RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
-                    remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
-
-            rat.setDaemon(true);
-            rat.start();
-
-            return;
-        }
-
-        if ((server_state != null) && ("session".equals(channelType)))
-        {
-            ServerConnectionCallback cb = null;
-            
-            synchronized (server_state)
-            {
-                cb = server_state.cb_conn;
-            }
-            
-            if (cb == null)
-            {
-                tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
-                        "Sessions are currently not enabled", "en").getPayload());
-                
-                return;
-            }
-            
-            final Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-                c.state = Channel.STATE_OPEN;
-                c.ss = new ServerSessionImpl(c);
-            }
-
-            PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
-                    c.localWindow, c.localMaxPacketSize);
-
-            tm.sendAsynchronousMessage(pcoc.getPayload());
-
-            c.ss.sscb = cb.acceptSession(c.ss);
-
-            return;
-        }
-
-        /* Tell the server that we have no idea what it is talking about */
-
-        PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
-                "Unknown channel type", "");
-
-        tm.sendAsynchronousMessage(pcof.getPayload());
-
-
-        log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
-    }
-
-    /* Starts the given runnable in a foreground (non-daemon) thread */
-    private void runAsync(Runnable r)
-    {
-        Thread t = new Thread(r);
-        t.start();        
-    }
-    
-    public void msgChannelRequest(byte[] msg, int msglen) throws IOException
-    {
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        int id = tr.readUINT32();
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
-
-        ServerSessionImpl server_session = null;
-
-        if (server_state != null)
-        {
-            synchronized (c)
-            {
-                server_session = c.ss;
-            }
-        }
-
-        String type = tr.readString("US-ASCII");
-        boolean wantReply = tr.readBoolean();
-
-        log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
-
-        if (type.equals("exit-status"))
-        {
-            if (wantReply != false)
-                throw new IOException(
-                        "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
-
-            int exit_status = tr.readUINT32();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-            synchronized (c)
-            {
-                c.exit_status = new Integer(exit_status);
-                c.notifyAll();
-            }
-
-            log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
-
-            return;
-        }
-
-        if ((server_state == null) && (type.equals("exit-signal")))
-        {
-            if (wantReply != false)
-                throw new IOException(
-                        "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
-
-            String signame = tr.readString("US-ASCII");
-            tr.readBoolean();
-            tr.readString();
-            tr.readString();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-            synchronized (c)
-            {
-                c.exit_signal = signame;
-                c.notifyAll();
-            }
-
-            log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
-
-            return;
-        }
-
-        if ((server_session != null) && (type.equals("pty-req")))
-        {
-            PtySettings pty = new PtySettings();
-
-            pty.term = tr.readString();
-            pty.term_width_characters = tr.readUINT32();
-            pty.term_height_characters = tr.readUINT32();
-            pty.term_width_pixels = tr.readUINT32();
-            pty.term_height_pixels = tr.readUINT32();
-            pty.terminal_modes = tr.readByteString();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestPtyReq(server_session, pty);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }            
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-        
-        if ((server_session != null) && (type.equals("subsystem")))
-        {
-            String command = tr.readString();
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestSubsystem(server_session, command);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-
-        if ((server_session != null) && (type.equals("shell")))
-        {
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestShell(server_session);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-        
-        if ((server_session != null) && (type.equals("exec")))
-        {
-            String command = tr.readString();
-            
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestExec(server_session, command);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-
-        /* We simply ignore unknown channel requests, however, if the server wants a reply,
-         * then we signal that we have no idea what it is about.
-         */
-
-        if (wantReply)
-        {
-            tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-        }
-
-        log.debug("Channel request '" + type + "' is not known, ignoring it");
-    }
-
-    public void msgChannelEOF(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
-    }
-
-    public void msgChannelClose(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.state = Channel.STATE_CLOSED;
-            c.setReasonClosed("Close requested by remote");
-            c.closeMessageRecv = true;
-
-            removeChannel(c.localID);
-
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
-    }
-
-    public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.successCounter++;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
-    }
-
-    public void msgChannelFailure(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.failedCounter++;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
-    }
-
-    public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
-    {
-        PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
-
-        Channel c = getChannel(sm.recipientChannelID);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
-                    + sm.recipientChannelID);
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPENING)
-                throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
-                        + sm.recipientChannelID);
-
-            c.remoteID = sm.senderChannelID;
-            c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
-            c.remoteMaxPacketSize = sm.maxPacketSize;
-            c.state = Channel.STATE_OPEN;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
-                + sm.senderChannelID + ")");
-    }
-
-    public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen < 5)
-            throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
-
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        int id = tr.readUINT32(); /* sender channel */
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
-
-        int reasonCode = tr.readUINT32();
-        String description = tr.readString("UTF-8");
-
-        String reasonCodeSymbolicName = null;
-
-        switch (reasonCode)
-        {
-            case 1:
-                reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
-                break;
-            case 2:
-                reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
-                break;
-            case 3:
-                reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
-                break;
-            case 4:
-                reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
-                break;
-            default:
-                reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
-        }
-
-        StringBuilder descriptionBuffer = new StringBuilder();
-        descriptionBuffer.append(description);
-
-        for (int i = 0; i < descriptionBuffer.length(); i++)
-        {
-            char cc = descriptionBuffer.charAt(i);
-
-            if ((cc >= 32) && (cc <= 126))
-                continue;
-            descriptionBuffer.setCharAt(i, '\uFFFD');
-        }
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.state = Channel.STATE_CLOSED;
-            c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
-                    + descriptionBuffer.toString() + "')");
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
-    }
-
-    public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
-    {
-        /* Currently we do not support any kind of global request */
-
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        String requestName = tr.readString();
-        boolean wantReply = tr.readBoolean();
-
-        if (wantReply)
-        {
-            byte[] reply_failure = new byte[1];
-            reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
-
-            tm.sendAsynchronousMessage(reply_failure);
-        }
-
-        /* We do not clean up the requestName String - that is OK for debug */
-
-        log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
-    }
-
-    public void msgGlobalSuccess() throws IOException
-    {
-        synchronized (channels)
-        {
-            globalSuccessCounter++;
-            channels.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_REQUEST_SUCCESS");
-    }
-
-    public void msgGlobalFailure() throws IOException
-    {
-        synchronized (channels)
-        {
-            globalFailedCounter++;
-            channels.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_REQUEST_FAILURE");
-    }
-
-    public void handleMessage(byte[] msg, int msglen) throws IOException
-    {
-        if (msg == null)
-        {
-
-            log.debug("HandleMessage: got shutdown");
-
-            synchronized (listenerThreads)
-            {
-                for (IChannelWorkerThread lat : listenerThreads)
-                {
-                    lat.stopWorking();
-                }
-                listenerThreadsAllowed = false;
-            }
-
-            synchronized (channels)
-            {
-                shutdown = true;
-
-                for (Channel c : channels)
-                {
-                    synchronized (c)
-                    {
-                        c.EOF = true;
-                        c.state = Channel.STATE_CLOSED;
-                        c.setReasonClosed("The connection is being shutdown");
-                        c.closeMessageRecv = true; /*
-                                                    * You never know, perhaps
-                                                    * we are waiting for a
-                                                    * pending close message
-                                                    * from the server...
-                                                    */
-                        c.notifyAll();
-                    }
-                }
-
-                channels.clear();
-                channels.notifyAll(); /* Notify global response waiters */
-                return;
-            }
-        }
-
-        switch (msg[0])
-        {
-            case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-                msgChannelOpenConfirmation(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
-                msgChannelWindowAdjust(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_DATA:
-                msgChannelData(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
-                msgChannelExtendedData(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_REQUEST:
-                msgChannelRequest(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_EOF:
-                msgChannelEOF(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_OPEN:
-                msgChannelOpen(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_CLOSE:
-                msgChannelClose(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_SUCCESS:
-                msgChannelSuccess(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_FAILURE:
-                msgChannelFailure(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
-                msgChannelOpenFailure(msg, msglen);
-                break;
-            case Packets.SSH_MSG_GLOBAL_REQUEST:
-                msgGlobalRequest(msg, msglen);
-                break;
-            case Packets.SSH_MSG_REQUEST_SUCCESS:
-                msgGlobalSuccess();
-                break;
-            case Packets.SSH_MSG_REQUEST_FAILURE:
-                msgGlobalFailure();
-                break;
-            default:
-                throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
-        }
-    }
-}
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java
deleted file mode 100644 (file)
index 9632670..0000000
+++ /dev/null
@@ -1,990 +0,0 @@
-/*
- * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.security.SecureRandom;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.ConnectionInfo;
-import ch.ethz.ssh2.ConnectionMonitor;
-import ch.ethz.ssh2.DHGexParameters;
-import ch.ethz.ssh2.HTTPProxyData;
-import ch.ethz.ssh2.HTTPProxyException;
-import ch.ethz.ssh2.ProxyData;
-import ch.ethz.ssh2.ServerHostKeyVerifier;
-import ch.ethz.ssh2.crypto.Base64;
-import ch.ethz.ssh2.crypto.CryptoWishList;
-import ch.ethz.ssh2.crypto.cipher.BlockCipher;
-import ch.ethz.ssh2.crypto.digest.MAC;
-import ch.ethz.ssh2.log.Logger;
-import ch.ethz.ssh2.packets.PacketDisconnect;
-import ch.ethz.ssh2.packets.Packets;
-import ch.ethz.ssh2.packets.TypesReader;
-import ch.ethz.ssh2.server.ServerConnectionState;
-import ch.ethz.ssh2.signature.DSAPrivateKey;
-import ch.ethz.ssh2.signature.RSAPrivateKey;
-import ch.ethz.ssh2.util.StringEncoder;
-import ch.ethz.ssh2.util.Tokenizer;
-
-/*
- * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
- * packets are allowed during kex exchange, on the other side we need to blindly
- * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
- * the next packet is not a channel data packet? Yes, we could check if it is in
- * the KEX range. But the standard says nothing about this. The OpenSSH guys
- * block local "normal" traffic during KEX. That's fine - however, they assume
- * that the other side is doing the same. During re-key, if they receive traffic
- * other than KEX, they become horribly irritated and kill the connection. Since
- * we are very likely going to communicate with OpenSSH servers, we have to play
- * the same game - even though we could do better.
- * 
- * btw: having stdout and stderr on the same channel, with a shared window, is
- * also a VERY good idea... =(
- */
-
-/**
- * TransportManager.
- *
- * @author Christian Plattner
- * @version $Id: TransportManager.java 47 2013-07-31 23:59:52Z cleondris@gmail.com $
- */
-public class TransportManager
-{
-    private static final Logger log = Logger.getLogger(TransportManager.class);
-
-    private static class HandlerEntry
-    {
-        MessageHandler mh;
-        int low;
-        int high;
-    }
-
-    private final List<AsynchronousEntry> asynchronousQueue = new Vector<AsynchronousEntry>();
-    private Thread asynchronousThread = null;
-    private boolean asynchronousPending = false;
-
-    class AsynchronousEntry
-    {
-        public byte[] msg;
-        public Runnable run;
-
-        public AsynchronousEntry(byte[] msg, Runnable run)
-        {
-            this.msg = msg;
-            this.run = run;
-        }
-    }
-
-    class AsynchronousWorker extends Thread
-    {
-        @Override
-        public void run()
-        {
-            while (true)
-            {
-                AsynchronousEntry item = null;
-
-                synchronized (asynchronousQueue)
-                {
-                    if (asynchronousQueue.size() == 0)
-                    {
-                                               /* Only now we may reset the flag, since we are sure that all queued items
-                                                * have been sent (there is a slight delay between de-queuing and sending,
-                                                * this is why we need this flag! See code below. Sending takes place outside
-                                                * of this lock, this is why a test for size()==0 (from another thread) does not ensure
-                                                * that all messages have been sent.
-                                                */
-
-                        asynchronousPending = false;
-
-                                               /* Notify any senders that they can proceed, all async messages have been delivered */
-
-                        asynchronousQueue.notifyAll();
-
-                                               /* After the queue is empty for about 2 seconds, stop this thread */
-
-                        try
-                        {
-                            asynchronousQueue.wait(2000);
-                        }
-                        catch (InterruptedException ignore)
-                        {
-                        }
-
-                        if (asynchronousQueue.size() == 0)
-                        {
-                            asynchronousThread = null;
-                            return;
-                        }
-                    }
-
-                    item = asynchronousQueue.remove(0);
-                }
-
-                               /* The following invocation may throw an IOException.
-                                * There is no point in handling it - it simply means
-                                * that the connection has a problem and we should stop
-                                * sending asynchronously messages. We do not need to signal that
-                                * we have exited (asynchronousThread = null): further
-                                * messages in the queue cannot be sent by this or any
-                                * other thread.
-                                * Other threads will sooner or later (when receiving or
-                                * sending the next message) get the same IOException and
-                                * get to the same conclusion.
-                                */
-
-                try
-                {
-                    sendMessageImmediate(item.msg);
-                }
-                catch (IOException e)
-                {
-                    return;
-                }
-
-                if (item.run != null)
-                {
-                    try
-                    {
-                        item.run.run();
-                    }
-                    catch (Exception ignore)
-                    {
-                    }
-
-                }
-            }
-        }
-    }
-
-    private Socket sock = new Socket();
-
-    private final Object connectionSemaphore = new Object();
-
-    private boolean flagKexOngoing = false;
-    private boolean connectionClosed = false;
-
-    private Throwable reasonClosedCause = null;
-
-    private TransportConnection tc;
-    private KexManager km;
-
-    private final List<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
-
-    private Thread receiveThread;
-
-    private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-    private boolean monitorsWereInformed = false;
-
-    /**
-     * There were reports that there are JDKs which use
-     * the resolver even though one supplies a dotted IP
-     * address in the Socket constructor. That is why we
-     * try to generate the InetAdress "by hand".
-     *
-     * @param host
-     * @return the InetAddress
-     * @throws UnknownHostException
-     */
-    private static InetAddress createInetAddress(String host) throws UnknownHostException
-    {
-               /* Check if it is a dotted IP4 address */
-
-        InetAddress addr = parseIPv4Address(host);
-
-        if (addr != null)
-        {
-            return addr;
-        }
-
-        return InetAddress.getByName(host);
-    }
-
-    private static InetAddress parseIPv4Address(String host) throws UnknownHostException
-    {
-        if (host == null)
-        {
-            return null;
-        }
-
-        String[] quad = Tokenizer.parseTokens(host, '.');
-
-        if ((quad == null) || (quad.length != 4))
-        {
-            return null;
-        }
-
-        byte[] addr = new byte[4];
-
-        for (int i = 0; i < 4; i++)
-        {
-            int part = 0;
-
-            if ((quad[i].length() == 0) || (quad[i].length() > 3))
-            {
-                return null;
-            }
-
-            for (int k = 0; k < quad[i].length(); k++)
-            {
-                char c = quad[i].charAt(k);
-
-                               /* No, Character.isDigit is not the same */
-                if ((c < '0') || (c > '9'))
-                {
-                    return null;
-                }
-
-                part = part * 10 + (c - '0');
-            }
-
-            if (part > 255) /* 300.1.2.3 is invalid =) */
-            {
-                return null;
-            }
-
-            addr[i] = (byte) part;
-        }
-
-        return InetAddress.getByAddress(host, addr);
-    }
-
-    public int getPacketOverheadEstimate()
-    {
-        return tc.getPacketOverheadEstimate();
-    }
-
-    public void setTcpNoDelay(boolean state) throws IOException
-    {
-        sock.setTcpNoDelay(state);
-    }
-
-    public void setSoTimeout(int timeout) throws IOException
-    {
-        sock.setSoTimeout(timeout);
-    }
-
-    public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException
-    {
-        return km.getOrWaitForConnectionInfo(kexNumber);
-    }
-
-    public Throwable getReasonClosedCause()
-    {
-        synchronized (connectionSemaphore)
-        {
-            return reasonClosedCause;
-        }
-    }
-
-    public byte[] getSessionIdentifier()
-    {
-        return km.sessionId;
-    }
-
-    public void close(Throwable cause, boolean useDisconnectPacket)
-    {
-        if (useDisconnectPacket == false)
-        {
-                       /* OK, hard shutdown - do not aquire the semaphore,
-                        * perhaps somebody is inside (and waits until the remote
-                        * side is ready to accept new data). */
-
-            try
-            {
-                sock.close();
-            }
-            catch (IOException ignore)
-            {
-            }
-
-                       /* OK, whoever tried to send data, should now agree that
-                        * there is no point in further waiting =)
-                        * It is safe now to aquire the semaphore.
-                        */
-        }
-
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed == false)
-            {
-                if (useDisconnectPacket == true)
-                {
-                    try
-                    {
-                        byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
-                                .getPayload();
-                        if (tc != null)
-                        {
-                            tc.sendMessage(msg);
-                        }
-                    }
-                    catch (IOException ignore)
-                    {
-                    }
-
-                    try
-                    {
-                        sock.close();
-                    }
-                    catch (IOException ignore)
-                    {
-                    }
-                }
-
-                connectionClosed = true;
-                reasonClosedCause = cause; /* may be null */
-            }
-            connectionSemaphore.notifyAll();
-        }
-
-               /* No check if we need to inform the monitors */
-
-        List<ConnectionMonitor> monitors = new Vector<ConnectionMonitor>();
-
-        synchronized (this)
-        {
-                       /* Short term lock to protect "connectionMonitors"
-                        * and "monitorsWereInformed"
-                        * (they may be modified concurrently)
-                        */
-
-            if (monitorsWereInformed == false)
-            {
-                monitorsWereInformed = true;
-                monitors.addAll(connectionMonitors);
-            }
-        }
-
-        for (ConnectionMonitor cmon : monitors)
-        {
-            try
-            {
-                cmon.connectionLost(reasonClosedCause);
-            }
-            catch (Exception ignore)
-            {
-            }
-        }
-    }
-
-    private static Socket establishConnection(String hostname, int port, ProxyData proxyData, int connectTimeout)
-            throws IOException
-    {
-               /* See the comment for createInetAddress() */
-
-        if (proxyData == null)
-        {
-            InetAddress addr = createInetAddress(hostname);
-            Socket s = new Socket();
-            s.connect(new InetSocketAddress(addr, port), connectTimeout);
-            return s;
-        }
-
-        if (proxyData instanceof HTTPProxyData)
-        {
-            HTTPProxyData pd = (HTTPProxyData) proxyData;
-
-                       /* At the moment, we only support HTTP proxies */
-
-            InetAddress addr = createInetAddress(pd.proxyHost);
-            Socket s = new Socket();
-            s.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
-
-                       /* OK, now tell the proxy where we actually want to connect to */
-
-            StringBuilder sb = new StringBuilder();
-
-            sb.append("CONNECT ");
-            sb.append(hostname);
-            sb.append(':');
-            sb.append(port);
-            sb.append(" HTTP/1.0\r\n");
-
-            if ((pd.proxyUser != null) && (pd.proxyPass != null))
-            {
-                String credentials = pd.proxyUser + ":" + pd.proxyPass;
-                char[] encoded = Base64.encode(StringEncoder.GetBytes(credentials));
-                sb.append("Proxy-Authorization: Basic ");
-                sb.append(encoded);
-                sb.append("\r\n");
-            }
-
-            if (pd.requestHeaderLines != null)
-            {
-                for (int i = 0; i < pd.requestHeaderLines.length; i++)
-                {
-                    if (pd.requestHeaderLines[i] != null)
-                    {
-                        sb.append(pd.requestHeaderLines[i]);
-                        sb.append("\r\n");
-                    }
-                }
-            }
-
-            sb.append("\r\n");
-
-            OutputStream out = s.getOutputStream();
-
-            out.write(StringEncoder.GetBytes(sb.toString()));
-            out.flush();
-
-                       /* Now parse the HTTP response */
-
-            byte[] buffer = new byte[1024];
-            InputStream in = s.getInputStream();
-
-            int len = ClientServerHello.readLineRN(in, buffer);
-
-            String httpReponse = StringEncoder.GetString(buffer, 0, len);
-
-            if (httpReponse.startsWith("HTTP/") == false)
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-                       /* "HTTP/1.X XYZ X" => 14 characters minimum */
-
-            if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            int errorCode = 0;
-
-            try
-            {
-                errorCode = Integer.parseInt(httpReponse.substring(9, 12));
-            }
-            catch (NumberFormatException ignore)
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            if ((errorCode < 0) || (errorCode > 999))
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            if (errorCode != 200)
-            {
-                throw new HTTPProxyException(httpReponse.substring(13), errorCode);
-            }
-
-                       /* OK, read until empty line */
-
-            while (true)
-            {
-                len = ClientServerHello.readLineRN(in, buffer);
-                if (len == 0)
-                {
-                    break;
-                }
-            }
-            return s;
-        }
-
-        throw new IOException("Unsupported ProxyData");
-    }
-
-    private void startReceiver() throws IOException
-    {
-        receiveThread = new Thread(new Runnable()
-        {
-            public void run()
-            {
-                try
-                {
-                    receiveLoop();
-                }
-                catch (Exception e)
-                {
-                    close(e, false);
-
-                    log.warning("Receive thread: error in receiveLoop: " + e.getMessage());
-                }
-
-                if (log.isDebugEnabled())
-                {
-                    log.debug("Receive thread: back from receiveLoop");
-                }
-
-                               /* Tell all handlers that it is time to say goodbye */
-
-                if (km != null)
-                {
-                    try
-                    {
-                        km.handleMessage(null, 0);
-                    }
-                    catch (IOException ignored)
-                    {
-                    }
-                }
-
-                for (HandlerEntry he : messageHandlers)
-                {
-                    try
-                    {
-                        he.mh.handleMessage(null, 0);
-                    }
-                    catch (Exception ignore)
-                    {
-                    }
-                }
-            }
-        });
-
-        receiveThread.setDaemon(true);
-        receiveThread.start();
-    }
-
-    public void clientInit(Socket socket, String softwareversion, CryptoWishList cwl,
-                           ServerHostKeyVerifier verifier, DHGexParameters dhgex, SecureRandom rnd) throws IOException
-    {
-               /* First, establish the TCP connection to the SSH-2 server */
-
-        sock = socket;
-
-               /* Parse the server line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
-                sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-        String hostname = sock.getInetAddress().getHostName();
-        int port = sock.getPort();
-
-        km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
-        km.initiateKEX(cwl, dhgex, null, null);
-
-        startReceiver();
-    }
-
-    public void clientInit(String hostname, int port, String softwareversion, CryptoWishList cwl,
-                           ServerHostKeyVerifier verifier, DHGexParameters dhgex, int connectTimeout, SecureRandom rnd,
-                           ProxyData proxyData) throws IOException
-    {
-               /* First, establish the TCP connection to the SSH-2 server */
-
-        sock = establishConnection(hostname, port, proxyData, connectTimeout);
-
-               /* Parse the server line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
-                sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-
-        km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
-        km.initiateKEX(cwl, dhgex, null, null);
-
-        startReceiver();
-    }
-
-    public void serverInit(ServerConnectionState state) throws IOException
-    {
-               /* TCP connection is already established */
-
-        this.sock = state.s;
-
-               /* Parse the client line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        state.csh = ClientServerHello.serverHello(state.softwareversion, sock.getInputStream(), sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), state.generator);
-
-        km = new ServerKexManager(state);
-        km.initiateKEX(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key);
-
-        startReceiver();
-    }
-
-    public void registerMessageHandler(MessageHandler mh, int low, int high)
-    {
-        HandlerEntry he = new HandlerEntry();
-        he.mh = mh;
-        he.low = low;
-        he.high = high;
-
-        synchronized (messageHandlers)
-        {
-            messageHandlers.add(he);
-        }
-    }
-
-    public void removeMessageHandler(MessageHandler mh, int low, int high)
-    {
-        synchronized (messageHandlers)
-        {
-            for (int i = 0; i < messageHandlers.size(); i++)
-            {
-                HandlerEntry he = messageHandlers.get(i);
-                if ((he.mh == mh) && (he.low == low) && (he.high == high))
-                {
-                    messageHandlers.remove(i);
-                    break;
-                }
-            }
-        }
-    }
-
-    public void sendKexMessage(byte[] msg) throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed)
-            {
-                throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
-            }
-
-            flagKexOngoing = true;
-
-            try
-            {
-                tc.sendMessage(msg);
-            }
-            catch (IOException e)
-            {
-                close(e, false);
-                throw e;
-            }
-        }
-    }
-
-    public void kexFinished() throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            flagKexOngoing = false;
-            connectionSemaphore.notifyAll();
-        }
-    }
-
-    /**
-     *
-     * @param cwl
-     * @param dhgex
-     * @param dsa may be null if this is a client connection
-     * @param rsa may be null if this is a client connection
-     * @throws IOException
-     */
-    public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex, DSAPrivateKey dsa, RSAPrivateKey rsa)
-            throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed)
-                               /* Inform the caller that there is no point in triggering a new kex */
-                throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
-        }
-
-        km.initiateKEX(cwl, dhgex, dsa, rsa);
-    }
-
-    public void changeRecvCipher(BlockCipher bc, MAC mac)
-    {
-        tc.changeRecvCipher(bc, mac);
-    }
-
-    public void changeSendCipher(BlockCipher bc, MAC mac)
-    {
-        tc.changeSendCipher(bc, mac);
-    }
-
-    public void sendAsynchronousMessage(byte[] msg) throws IOException
-    {
-        sendAsynchronousMessage(msg, null);
-    }
-
-    public void sendAsynchronousMessage(byte[] msg, Runnable run) throws IOException
-    {
-        synchronized (asynchronousQueue)
-        {
-            asynchronousQueue.add(new AsynchronousEntry(msg, run));
-            asynchronousPending = true;
-
-                       /* This limit should be flexible enough. We need this, otherwise the peer
-                        * can flood us with global requests (and other stuff where we have to reply
-                        * with an asynchronous message) and (if the server just sends data and does not
-                        * read what we send) this will probably put us in a low memory situation
-                        * (our send queue would grow and grow and...) */
-
-            if (asynchronousQueue.size() > 100)
-            {
-                throw new IOException("Error: the peer is not consuming our asynchronous replies.");
-            }
-
-                       /* Check if we have an asynchronous sending thread */
-
-            if (asynchronousThread == null)
-            {
-                asynchronousThread = new AsynchronousWorker();
-                asynchronousThread.setDaemon(true);
-                asynchronousThread.start();
-
-                               /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
-            }
-
-            asynchronousQueue.notifyAll();
-        }
-    }
-
-    public void setConnectionMonitors(List<ConnectionMonitor> monitors)
-    {
-        synchronized (this)
-        {
-            connectionMonitors = new Vector<ConnectionMonitor>();
-            connectionMonitors.addAll(monitors);
-        }
-    }
-
-    /**
-     * True if no response message expected.
-     */
-    private boolean idle;
-
-    /**
-     * Send a message but ensure that all queued messages are being sent first.
-     *
-     * @param msg
-     * @throws IOException
-     */
-    public void sendMessage(byte[] msg) throws IOException
-    {
-        synchronized (asynchronousQueue)
-        {
-            while (asynchronousPending)
-            {
-                try
-                {
-                    asynchronousQueue.wait(1000);
-                }
-                catch (InterruptedException e)
-                {
-                }
-            }
-        }
-
-        sendMessageImmediate(msg);
-    }
-
-    /**
-     * Send message, ignore queued async messages that have not been delivered yet.
-     * Will be called directly from the asynchronousThread thread.
-     *
-     * @param msg
-     * @throws IOException
-     */
-    public void sendMessageImmediate(byte[] msg) throws IOException
-    {
-        if (Thread.currentThread() == receiveThread)
-        {
-            throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
-        }
-
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (connectionSemaphore)
-            {
-                while (true)
-                {
-                    if (connectionClosed)
-                    {
-                        throw (IOException) new IOException("Sorry, this connection is closed.")
-                                .initCause(reasonClosedCause);
-                    }
-
-                    if (flagKexOngoing == false)
-                    {
-                        break;
-                    }
-
-                    try
-                    {
-                        connectionSemaphore.wait();
-                    }
-                    catch (InterruptedException e)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                try
-                {
-                    tc.sendMessage(msg);
-                    idle = false;
-                }
-                catch (IOException e)
-                {
-                    close(e, false);
-                    throw e;
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public void receiveLoop() throws IOException
-    {
-        byte[] msg = new byte[35000];
-
-        while (true)
-        {
-            int msglen;
-            try
-            {
-                msglen = tc.receiveMessage(msg, 0, msg.length);
-            }
-            catch (SocketTimeoutException e)
-            {
-                // Timeout in read
-                if (idle)
-                {
-                    log.debug("Ignoring socket timeout");
-                    continue;
-                }
-                throw e;
-            }
-            idle = true;
-
-            int type = msg[0] & 0xff;
-
-            if (type == Packets.SSH_MSG_IGNORE)
-            {
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_DEBUG)
-            {
-                if (log.isDebugEnabled())
-                {
-                    TypesReader tr = new TypesReader(msg, 0, msglen);
-                    tr.readByte();
-                    tr.readBoolean();
-                    StringBuilder debugMessageBuffer = new StringBuilder();
-                    debugMessageBuffer.append(tr.readString("UTF-8"));
-
-                    for (int i = 0; i < debugMessageBuffer.length(); i++)
-                    {
-                        char c = debugMessageBuffer.charAt(i);
-
-                        if ((c >= 32) && (c <= 126))
-                        {
-                            continue;
-                        }
-                        debugMessageBuffer.setCharAt(i, '\uFFFD');
-                    }
-
-                    log.debug("DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
-                }
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_UNIMPLEMENTED)
-            {
-                throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
-            }
-
-            if (type == Packets.SSH_MSG_DISCONNECT)
-            {
-                TypesReader tr = new TypesReader(msg, 0, msglen);
-                tr.readByte();
-                int reason_code = tr.readUINT32();
-                StringBuilder reasonBuffer = new StringBuilder();
-                reasonBuffer.append(tr.readString("UTF-8"));
-
-                               /*
-                                * Do not get fooled by servers that send abnormal long error
-                                * messages
-                                */
-
-                if (reasonBuffer.length() > 255)
-                {
-                    reasonBuffer.setLength(255);
-                    reasonBuffer.setCharAt(254, '.');
-                    reasonBuffer.setCharAt(253, '.');
-                    reasonBuffer.setCharAt(252, '.');
-                }
-
-                               /*
-                                * Also, check that the server did not send characters that may
-                                * screw up the receiver -> restrict to reasonable US-ASCII
-                                * subset -> "printable characters" (ASCII 32 - 126). Replace
-                                * all others with 0xFFFD (UNICODE replacement character).
-                                */
-
-                for (int i = 0; i < reasonBuffer.length(); i++)
-                {
-                    char c = reasonBuffer.charAt(i);
-
-                    if ((c >= 32) && (c <= 126))
-                    {
-                        continue;
-                    }
-                    reasonBuffer.setCharAt(i, '\uFFFD');
-                }
-
-                throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
-                        + reasonBuffer.toString());
-            }
-
-                       /*
-                        * Is it a KEX Packet?
-                        */
-
-            if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
-                    || ((type >= 30) && (type <= 49)))
-            {
-                km.handleMessage(msg, msglen);
-                continue;
-            }
-
-            MessageHandler mh = null;
-
-            for (int i = 0; i < messageHandlers.size(); i++)
-            {
-                HandlerEntry he = messageHandlers.get(i);
-                if ((he.low <= type) && (type <= he.high))
-                {
-                    mh = he.mh;
-                    break;
-                }
-            }
-
-            if (mh == null)
-            {
-                throw new IOException("Unexpected SSH message (type " + type + ")");
-            }
-
-            mh.handleMessage(msg, msglen);
-        }
-    }
-}