diff --git a/pom.xml b/pom.xml index c6b7aaa..4076472 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.example.sshd echo-sshd-server - 1.4.4 + 1.4.5 ECHO SSH SERVER Learning Apache Mina SSHD library diff --git a/src/main/java/com/example/sshd/core/EchoSessionListener.java b/src/main/java/com/example/sshd/core/EchoSessionListener.java index ab8bb13..aecf521 100644 --- a/src/main/java/com/example/sshd/core/EchoSessionListener.java +++ b/src/main/java/com/example/sshd/core/EchoSessionListener.java @@ -4,18 +4,14 @@ import java.net.InetSocketAddress; import java.util.List; 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; +import com.example.sshd.service.GeoIpLocator; import com.example.sshd.service.JdbcService; @Component @@ -31,17 +27,11 @@ public class EchoSessionListener implements SessionListener { Map ipInfoMapping; @Autowired - CloseableHttpAsyncClient asyncClient; + GeoIpLocator geoIpLocator; @Autowired JdbcService jdbcService; - @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); @@ -55,7 +45,7 @@ public class EchoSessionListener implements SessionListener { logger.info("new session: {} -> {}", remoteIpAddress, session); remoteSessionMapping.put(remoteIpAddress, session); if (!ipInfoMapping.containsKey(remoteIpAddress)) { - List> ipInfoList = jdbcService.getRemoteIpInfo(remoteIpAddress); + List> ipInfoList = jdbcService.getAllRemoteIpInfo(remoteIpAddress); if (!ipInfoList.isEmpty()) { ipInfoMapping.put(remoteIpAddress, (String) ipInfoList.get(0).get("remote_ip_info")); } @@ -70,32 +60,7 @@ public class EchoSessionListener implements SessionListener { 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()); - int inserted = jdbcService.insertRemoteIpInfo(remoteIpAddress, result.getBodyText()); - ipInfoLogger.info("[{}] {}, inserted = {}", remoteIpAddress, - ipInfoMapping.get(remoteIpAddress), inserted); - } - - @Override - public void failed(Exception exception) { - logger.info("[{}] asyncClient.execute failed, exception: {}", remoteIpAddress, - exception); - } - - @Override - public void cancelled() { - logger.info("[{}] asyncClient.execute cancelled.", remoteIpAddress); - } - }); + geoIpLocator.asyncUpdateIpLocationInfo(remoteIpAddress); } else { ipInfoLogger.debug("[{}] {}", remoteIpAddress, ipInfoMapping.get(remoteIpAddress)); } diff --git a/src/main/java/com/example/sshd/service/GeoIpLocator.java b/src/main/java/com/example/sshd/service/GeoIpLocator.java new file mode 100644 index 0000000..b3afcae --- /dev/null +++ b/src/main/java/com/example/sshd/service/GeoIpLocator.java @@ -0,0 +1,91 @@ +package com.example.sshd.service; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +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.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +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.Service; + +@Service +public class GeoIpLocator { + + private static final Logger logger = LoggerFactory.getLogger(GeoIpLocator.class); + private static final Logger ipInfoLogger = LoggerFactory.getLogger("ip_info"); + + @Autowired + Map ipInfoMapping; + + @Autowired + JdbcService jdbcService; + + @Autowired + CloseableHttpAsyncClient asyncClient; + + @Autowired + CloseableHttpClient httpClient; + + @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; + + public String getIpLocationInfo(String remoteIpAddress) { + HttpGet httpGet = new HttpGet(String.format(ipInfoApiUrl, remoteIpAddress)); + try { + return httpClient.execute(httpGet, new HttpClientResponseHandler() { + + @Override + public String handleResponse(ClassicHttpResponse result) throws HttpException, IOException { + String body = new String(result.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); + logger.info("[{}] asyncClient.execute completed, response code: {}, body: {}", remoteIpAddress, + result.getCode(), body); + return body; + } + + }); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public void asyncUpdateIpLocationInfo(String 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()); + int inserted = jdbcService.insertRemoteIpInfo(remoteIpAddress, result.getBodyText()); + ipInfoLogger.info("[{}] {}, inserted = {}", remoteIpAddress, ipInfoMapping.get(remoteIpAddress), + inserted); + } + + @Override + public void failed(Exception exception) { + logger.info("[{}] asyncClient.execute failed, exception: {}", remoteIpAddress, exception); + } + + @Override + public void cancelled() { + logger.info("[{}] asyncClient.execute cancelled.", remoteIpAddress); + } + }); + } +} diff --git a/src/main/java/com/example/sshd/service/JdbcService.java b/src/main/java/com/example/sshd/service/JdbcService.java index 94d168e..883c580 100644 --- a/src/main/java/com/example/sshd/service/JdbcService.java +++ b/src/main/java/com/example/sshd/service/JdbcService.java @@ -30,7 +30,19 @@ public class JdbcService { jdbcTemplate.execute(createRemoteIpLookupIndexSql); } - public List> getRemoteIpInfo(String remoteIp) { + public String getRemoteIpInfo(String remoteIp) { + var result = jdbcTemplate.query( + "SELECT id, remote_ip_address, remote_ip_info from public.remote_ip_lookup WHERE remote_ip_address = ? ", + new RowMapper() { + @Override + public String mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getString(3); + } + }, remoteIp); + return result.isEmpty() ? null : result.get(0); + } + + public List> getAllRemoteIpInfo(String remoteIp) { return jdbcTemplate.query( "SELECT id, remote_ip_address, remote_ip_info from public.remote_ip_lookup WHERE remote_ip_address = ? ", new RowMapper>() { diff --git a/src/main/java/com/example/sshd/service/ReplyService.java b/src/main/java/com/example/sshd/service/ReplyService.java index 9aea589..0a4f02c 100644 --- a/src/main/java/com/example/sshd/service/ReplyService.java +++ b/src/main/java/com/example/sshd/service/ReplyService.java @@ -40,6 +40,9 @@ public class ReplyService { @Autowired JdbcService jdbcService; + @Autowired + GeoIpLocator geoIpLocator; + public boolean replyToCommand(String command, OutputStream out, String prompt, ServerSession session) throws IOException { @@ -56,9 +59,11 @@ public class ReplyService { out.write(String.format("\r\n%s\r\n%s", ipInfoMapping.toString(), prompt).getBytes()); } else if (StringUtils.split(command.trim(), " ").length == 2 && StringUtils.equalsIgnoreCase(StringUtils.split(command.trim(), " ")[0], "get_geolocation")) { + String remoteIpInfo = StringUtils.getIfBlank( + jdbcService.getRemoteIpInfo(StringUtils.split(command.trim(), " ")[1]), + () -> geoIpLocator.getIpLocationInfo(StringUtils.split(command.trim(), " ")[1])); logger.info("[{}] get_geolocation command detected: {}", cmdHash, command.trim()); - out.write(String.format("\r\n%s\r\n%s", - jdbcService.getRemoteIpInfo(StringUtils.split(command.trim(), " ")[1]), prompt).getBytes()); + out.write(String.format("\r\n%s\r\n%s", remoteIpInfo, prompt).getBytes()); } else if (StringUtils.equalsIgnoreCase(command.trim(), "all_geolocations")) { logger.info("[{}] all_geolocations command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", jdbcService.getAllRemoteIpInfo(), prompt).getBytes());