diff --git a/conf/log4j2.xml b/conf/log4j2.xml
index f0fde93..d0cc96c 100644
--- a/conf/log4j2.xml
+++ b/conf/log4j2.xml
@@ -23,11 +23,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 2eed628..0f51583 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.example.sshd
echo-sshd-server
- 1.1.2
+ 1.1.3
ECHO SSH SERVER
Learning Apache Mina SSHD library
@@ -66,6 +66,10 @@
org.apache.commons
commons-lang3
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
diff --git a/readme.md b/readme.md
index 074fee0..b16a56a 100644
--- a/readme.md
+++ b/readme.md
@@ -8,4 +8,4 @@ I share this project so that others can learn this as well.
mvn clean package
## How to run
-java -jar echo-sshd-server-.jar
+java -jar echo-sshd-server-{version}.jar
diff --git a/src/main/java/com/example/sshd/config/AppConfig.java b/src/main/java/com/example/sshd/config/AppConfig.java
index cbc418a..7aca54a 100644
--- a/src/main/java/com/example/sshd/config/AppConfig.java
+++ b/src/main/java/com/example/sshd/config/AppConfig.java
@@ -4,6 +4,9 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.sshd.common.Session;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
@@ -18,4 +21,18 @@ public class AppConfig {
public Map remoteSessionMapping() {
return Collections.synchronizedMap(new HashMap<>());
}
+
+ @Bean
+ @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+ public Map ipInfoMapping() {
+ return Collections.synchronizedMap(new HashMap<>());
+ }
+
+ @Bean
+ public CloseableHttpAsyncClient asyncClient() {
+ final IOReactorConfig ioReactorConfig = IOReactorConfig.custom().build();
+ final CloseableHttpAsyncClient client = HttpAsyncClients.custom().setIOReactorConfig(ioReactorConfig).build();
+ client.start();
+ return client;
+ }
}
diff --git a/src/main/java/com/example/sshd/core/EchoSessionListener.java b/src/main/java/com/example/sshd/core/EchoSessionListener.java
index fc701af..28f5788 100644
--- a/src/main/java/com/example/sshd/core/EchoSessionListener.java
+++ b/src/main/java/com/example/sshd/core/EchoSessionListener.java
@@ -3,21 +3,39 @@ package com.example.sshd.core;
import java.net.InetSocketAddress;
import java.util.Map;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.SessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class EchoSessionListener implements SessionListener {
private static final Logger logger = LoggerFactory.getLogger(EchoSessionListener.class);
+ private static final Logger ipInfoLogger = LoggerFactory.getLogger("ip_info");
@Autowired
Map remoteSessionMapping;
+ @Autowired
+ Map ipInfoMapping;
+
+ @Autowired
+ CloseableHttpAsyncClient asyncClient;
+
+ @Value("${ssh-server.ip-info-api.url:http://ip-api.com/json/%s}")
+ private String ipInfoApiUrl;
+
+ @Value("${ssh-server.ip-info-api.method:GET}")
+ private String ipInfoApiMethod;
+
@Override
public void sessionCreated(Session session) {
logger.info("sessionCreated: {}", session);
@@ -36,6 +54,36 @@ public class EchoSessionListener implements SessionListener {
@Override
public void sessionEvent(Session session, Event event) {
logger.info("sessionEvent: {}, event: {}", session, event);
+ if (session.getIoSession().getRemoteAddress() instanceof InetSocketAddress && event == Event.KexCompleted) {
+ InetSocketAddress remoteAddress = (InetSocketAddress) session.getIoSession().getRemoteAddress();
+ String remoteIpAddress = remoteAddress.getAddress().getHostAddress();
+ if (!ipInfoMapping.containsKey(remoteIpAddress)) {
+ asyncClient.execute(
+ SimpleHttpRequest.create(ipInfoApiMethod, String.format(ipInfoApiUrl, remoteIpAddress)),
+ new FutureCallback() {
+
+ @Override
+ public void completed(SimpleHttpResponse result) {
+ logger.info("[{}] asyncClient.execute completed, result: {}, content-type: {}, body: {}",
+ remoteIpAddress, result, result.getContentType(), result.getBodyText());
+ ipInfoMapping.put(remoteIpAddress, result.getBodyText());
+ ipInfoLogger.info("[{}] {}", remoteIpAddress, ipInfoMapping.get(remoteIpAddress));
+ }
+
+ @Override
+ public void failed(Exception exception) {
+ logger.info("[{}] asyncClient.execute failed, exception: {}", remoteIpAddress, exception);
+ }
+
+ @Override
+ public void cancelled() {
+ logger.info("[{}] asyncClient.execute cancelled.", remoteIpAddress);
+ }
+ });
+ } else {
+ ipInfoLogger.debug("[{}] {}", remoteIpAddress, ipInfoMapping.get(remoteIpAddress));
+ }
+ }
}
@Override
diff --git a/src/main/java/com/example/sshd/core/EchoShellFactory.java b/src/main/java/com/example/sshd/core/EchoShellFactory.java
index 5e6eaf5..5b474e0 100644
--- a/src/main/java/com/example/sshd/core/EchoShellFactory.java
+++ b/src/main/java/com/example/sshd/core/EchoShellFactory.java
@@ -15,7 +15,6 @@ import org.apache.sshd.server.ExitCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.example.sshd.util.ReplyUtil;
@@ -26,13 +25,10 @@ public class EchoShellFactory implements Factory {
private static final Logger logger = LoggerFactory.getLogger(EchoShellFactory.class);
@Autowired
- Properties hashReplies;
-
- @Autowired
- Properties regexMapping;
+ ReplyUtil replyUtil;
@Autowired
- ApplicationContext applicationContext;
+ Properties hashReplies;
@Override
public Command create() {
@@ -41,28 +37,12 @@ public class EchoShellFactory implements Factory {
public class EchoShell implements Command, Runnable {
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback callback;
- private Environment environment;
- private Thread thread;
-
- public InputStream getIn() {
- return in;
- }
-
- public OutputStream getOut() {
- return out;
- }
-
- public OutputStream getErr() {
- return err;
- }
-
- public Environment getEnvironment() {
- return environment;
- }
+ protected InputStream in;
+ protected OutputStream out;
+ protected OutputStream err;
+ protected ExitCallback callback;
+ protected Environment environment;
+ protected Thread thread;
@Override
public void setInputStream(InputStream in) {
@@ -87,6 +67,7 @@ public class EchoShellFactory implements Factory {
@Override
public void start(Environment env) throws IOException {
environment = env;
+ logger.info("environment: {}", environment.getEnv());
thread = new Thread(this, UUID.randomUUID().toString());
thread.start();
}
@@ -109,7 +90,7 @@ public class EchoShellFactory implements Factory {
while (!Thread.currentThread().isInterrupted()) {
int s = r.read();
if (s == 13 || s == 10) {
- if (!ReplyUtil.replyToCommand(command, out, prompt, hashReplies, regexMapping)) {
+ if (!replyUtil.replyToCommand(command, out, prompt)) {
out.flush();
return;
}
diff --git a/src/main/java/com/example/sshd/core/OnetimeCommand.java b/src/main/java/com/example/sshd/core/OnetimeCommand.java
index 966a4a4..4d16278 100644
--- a/src/main/java/com/example/sshd/core/OnetimeCommand.java
+++ b/src/main/java/com/example/sshd/core/OnetimeCommand.java
@@ -3,7 +3,6 @@ package com.example.sshd.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Properties;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
@@ -18,12 +17,9 @@ import com.example.sshd.util.ReplyUtil;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class OnetimeCommand implements Command {
-
- @Autowired
- Properties hashReplies;
@Autowired
- Properties regexMapping;
+ ReplyUtil replyUtil;
private InputStream in;
private OutputStream out;
@@ -75,7 +71,7 @@ public class OnetimeCommand implements Command {
@Override
public void start(Environment env) throws IOException {
environment = env;
- ReplyUtil.replyToCommand(command, out, "", hashReplies, regexMapping);
+ replyUtil.replyToCommand(command, out, "");
out.flush();
callback.onExit(0);
}
diff --git a/src/main/java/com/example/sshd/util/ReplyUtil.java b/src/main/java/com/example/sshd/util/ReplyUtil.java
index 238c14b..6051369 100644
--- a/src/main/java/com/example/sshd/util/ReplyUtil.java
+++ b/src/main/java/com/example/sshd/util/ReplyUtil.java
@@ -10,14 +10,22 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+@Component
public class ReplyUtil {
private static final Logger logger = LoggerFactory.getLogger(ReplyUtil.class);
private static final Logger notFoundLogger = LoggerFactory.getLogger("not_found");
- public static boolean replyToCommand(String command, OutputStream out, String prompt, Properties hashReplies,
- Properties regexMapping) throws IOException {
+ @Autowired
+ Properties hashReplies;
+
+ @Autowired
+ Properties regexMapping;
+
+ public boolean replyToCommand(String command, OutputStream out, String prompt) throws IOException {
String cmdHash = DigestUtils.md5Hex(command.trim()).toUpperCase();