提交Git

This commit is contained in:
Peijun Zhao
2026-05-10 12:59:00 +08:00
commit b0aaea9fb3
2544 changed files with 545276 additions and 0 deletions
@@ -0,0 +1,423 @@
package com.aluer.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.net.InetAddress;
@Component
public class DNSSecurityService {
private static final Logger logger = LoggerFactory.getLogger(DNSSecurityService.class);
private final Map<String, DNSQueryRecord> queryCache = new ConcurrentHashMap<>();
private final Map<String, List<ClientQueryHistory>> clientQueryHistory = new ConcurrentHashMap<>();
private final Map<String, DNSZone> zones = new ConcurrentHashMap<>();
private final Queue<DNSEvent> eventLog = new ConcurrentLinkedQueue<>();
private final AtomicLong totalQueries = new AtomicLong(0);
private final AtomicLong blockedQueries = new AtomicLong(0);
private final AtomicLong suspiciousQueries = new AtomicLong(0);
private static final int MAX_QUERIES_PER_MINUTE = 60;
private static final int MAX_QUERIES_PER_HOUR = 1000;
private static final int CACHE_TTL_SECONDS = 300;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final Set<String> blockedDomains = new HashSet<>(Arrays.asList(
"malware.com", "phishing.com", "ransomware.com", "trojan.com",
"coinminer.com", "adservice.com", "tracker.com", "spyware.com"
));
private final Set<String> suspiciousTLDs = new HashSet<>(Arrays.asList(
".tk", ".ml", ".ga", ".cf", ".gq", ".xyz", ".top", ".work"
));
public DNSSecurityService() {
initializeDefaultZones();
}
private void initializeDefaultZones() {
DNSZone minecraftZone = new DNSZone("minecraft");
minecraftZone.addRecord("auth.mojang.com", "13.107.42.20");
minecraftZone.addRecord("sessionserver.mojang.com", "13.107.42.20");
minecraftZone.addRecord("api.mojang.com", "13.107.42.20");
minecraftZone.addRecord("textures.minecraft.net", "13.107.42.20");
zones.put("minecraft", minecraftZone);
DNSZone localZone = new DNSZone("local");
localZone.addRecord("localhost", "127.0.0.1");
zones.put("local", localZone);
}
public DNSQueryResult processQuery(String clientIP, String domainName, int queryType) {
DNSQueryResult result = new DNSQueryResult();
result.setClientIP(clientIP);
result.setDomainName(domainName);
result.setQueryType(queryType);
result.setTimestamp(LocalDateTime.now());
totalQueries.incrementAndGet();
if (isRateLimited(clientIP)) {
result.setBlocked(true);
result.setReason("Rate limit exceeded");
blockedQueries.incrementAndGet();
logEvent(clientIP, domainName, "BLOCKED", "Rate limit exceeded");
return result;
}
if (isDomainBlocked(domainName)) {
result.setBlocked(true);
result.setReason("Blocked domain");
result.setBlocked(true);
blockedQueries.incrementAndGet();
logEvent(clientIP, domainName, "BLOCKED", "Blocked domain");
return result;
}
if (isSuspiciousDomain(domainName)) {
result.setSuspicious(true);
result.setWarning("Suspicious TLD or pattern");
suspiciousQueries.incrementAndGet();
logEvent(clientIP, domainName, "SUSPICIOUS", "Suspicious domain pattern");
}
if (isDNSSplitTunneling(clientIP, domainName)) {
result.setSuspicious(true);
result.setWarning("Possible DNS tunnel detected");
suspiciousQueries.incrementAndGet();
logEvent(clientIP, domainName, "SUSPICIOUS", "Possible DNS tunnel");
}
DNSQueryRecord record = new DNSQueryRecord(clientIP, domainName, queryType, LocalDateTime.now());
queryCache.put(domainName + ":" + clientIP, record);
List<ClientQueryHistory> history = clientQueryHistory.computeIfAbsent(clientIP, k -> new ArrayList<>());
history.add(new ClientQueryHistory(domainName, LocalDateTime.now()));
if (history.size() > 1000) {
history.subList(0, 500).clear();
}
String resolvedIP = resolveDomain(domainName);
result.setResolvedIP(resolvedIP);
result.setBlocked(false);
logEvent(clientIP, domainName, "ALLOWED", "Query allowed");
return result;
}
private boolean isRateLimited(String clientIP) {
List<ClientQueryHistory> history = clientQueryHistory.get(clientIP);
if (history == null) return false;
LocalDateTime oneMinuteAgo = LocalDateTime.now().minusMinutes(1);
long queriesLastMinute = history.stream()
.filter(q -> q.getTimestamp().isAfter(oneMinuteAgo))
.count();
if (queriesLastMinute > MAX_QUERIES_PER_MINUTE) {
return true;
}
LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
long queriesLastHour = history.stream()
.filter(q -> q.getTimestamp().isAfter(oneHourAgo))
.count();
return queriesLastHour > MAX_QUERIES_PER_HOUR;
}
private boolean isDomainBlocked(String domainName) {
String lowerDomain = domainName.toLowerCase();
for (String blocked : blockedDomains) {
if (lowerDomain.endsWith(blocked) || lowerDomain.equals(blocked)) {
return true;
}
}
return false;
}
private boolean isSuspiciousDomain(String domainName) {
String lowerDomain = domainName.toLowerCase();
for (String tld : suspiciousTLDs) {
if (lowerDomain.endsWith(tld)) {
return true;
}
}
if (lowerDomain.length() > 50) {
return true;
}
int dots = lowerDomain.split("\\.").length - 1;
if (dots > 5) {
return true;
}
if (lowerDomain.contains("----") || lowerDomain.contains("----")) {
return true;
}
return false;
}
private boolean isDNSSplitTunneling(String clientIP, String domainName) {
List<ClientQueryHistory> history = clientQueryHistory.get(clientIP);
if (history == null || history.size() < 10) return false;
LocalDateTime recent = LocalDateTime.now().minusMinutes(5);
long recentQueries = history.stream()
.filter(q -> q.getTimestamp().isAfter(recent))
.count();
if (recentQueries < 100) return false;
Set<String> uniqueDomains = history.stream()
.filter(q -> q.getTimestamp().isAfter(recent))
.map(ClientQueryHistory::getDomain)
.collect(java.util.stream.Collectors.toSet());
double entropy = calculateEntropy(String.join(".", uniqueDomains));
return entropy > 4.5;
}
private double calculateEntropy(String data) {
if (data == null || data.isEmpty()) return 0;
Map<Character, Long> frequency = new HashMap<>();
for (char c : data.toCharArray()) {
frequency.merge(c, 1L, Long::sum);
}
double entropy = 0;
int length = data.length();
for (long count : frequency.values()) {
double probability = (double) count / length;
entropy -= probability * (Math.log(probability) / Math.log(2));
}
return entropy;
}
private String resolveDomain(String domainName) {
for (DNSZone zone : zones.values()) {
String ip = zone.resolve(domainName);
if (ip != null) {
return ip;
}
}
try {
InetAddress address = InetAddress.getByName(domainName);
return address.getHostAddress();
} catch (Exception e) {
return null;
}
}
public void addBlockedDomain(String domain) {
blockedDomains.add(domain.toLowerCase());
logger.info("Added blocked domain: {}", domain);
}
public void removeBlockedDomain(String domain) {
blockedDomains.remove(domain.toLowerCase());
logger.info("Removed blocked domain: {}", domain);
}
public void addZone(String zoneName, Map<String, String> records) {
DNSZone zone = new DNSZone(zoneName);
for (Map.Entry<String, String> entry : records.entrySet()) {
zone.addRecord(entry.getKey(), entry.getValue());
}
zones.put(zoneName, zone);
logger.info("Added DNS zone: {}", zoneName);
}
public Map<String, Object> getStats() {
Map<String, Object> stats = new HashMap<>();
stats.put("totalQueries", totalQueries.get());
stats.put("blockedQueries", blockedQueries.get());
stats.put("suspiciousQueries", suspiciousQueries.get());
stats.put("cachedQueries", queryCache.size());
stats.put("activeClients", clientQueryHistory.size());
stats.put("zonesConfigured", zones.size());
stats.put("blockedDomains", blockedDomains.size());
return stats;
}
public Map<String, DNSQueryRecord> getRecentQueries(int limit) {
Map<String, DNSQueryRecord> recent = new LinkedHashMap<>();
int count = 0;
for (Map.Entry<String, DNSQueryRecord> entry : queryCache.entrySet()) {
if (count++ >= limit) break;
recent.put(entry.getKey(), entry.getValue());
}
return recent;
}
public List<String> getTopQueriedDomains(int limit) {
Map<String, Long> domainCounts = new HashMap<>();
for (DNSQueryRecord record : queryCache.values()) {
String domain = record.getDomainName();
domainCounts.merge(domain, 1L, Long::sum);
}
return domainCounts.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(limit)
.map(Map.Entry::getKey)
.collect(java.util.stream.Collectors.toList());
}
public Map<String, Long> getClientQueryStats(String clientIP) {
List<ClientQueryHistory> history = clientQueryHistory.get(clientIP);
if (history == null) {
return new HashMap<>();
}
Map<String, Long> stats = new HashMap<>();
stats.put("totalQueries", (long) history.size());
LocalDateTime oneMinuteAgo = LocalDateTime.now().minusMinutes(1);
long recentQueries = history.stream()
.filter(q -> q.getTimestamp().isAfter(oneMinuteAgo))
.count();
stats.put("queriesLastMinute", recentQueries);
return stats;
}
private void logEvent(String clientIP, String domain, String eventType, String details) {
DNSEvent event = new DNSEvent(clientIP, domain, eventType, details, LocalDateTime.now());
eventLog.offer(event);
if (eventLog.size() > 5000) {
eventLog.poll();
}
}
public List<DNSEvent> getRecentEvents(int limit) {
List<DNSEvent> events = new ArrayList<>();
int count = 0;
for (DNSEvent event : eventLog) {
if (count++ >= limit) break;
events.add(event);
}
return events;
}
public static class DNSQueryResult {
private String clientIP;
private String domainName;
private int queryType;
private String resolvedIP;
private boolean blocked;
private boolean suspicious;
private String reason;
private String warning;
private LocalDateTime timestamp;
public String getClientIP() { return clientIP; }
public void setClientIP(String clientIP) { this.clientIP = clientIP; }
public String getDomainName() { return domainName; }
public void setDomainName(String domainName) { this.domainName = domainName; }
public int getQueryType() { return queryType; }
public void setQueryType(int queryType) { this.queryType = queryType; }
public String getResolvedIP() { return resolvedIP; }
public void setResolvedIP(String resolvedIP) { this.resolvedIP = resolvedIP; }
public boolean isBlocked() { return blocked; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public boolean isSuspicious() { return suspicious; }
public void setSuspicious(boolean suspicious) { this.suspicious = suspicious; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
public String getWarning() { return warning; }
public void setWarning(String warning) { this.warning = warning; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}
public static class DNSQueryRecord {
private final String clientIP;
private final String domainName;
private final int queryType;
private final LocalDateTime timestamp;
public DNSQueryRecord(String clientIP, String domainName, int queryType, LocalDateTime timestamp) {
this.clientIP = clientIP;
this.domainName = domainName;
this.queryType = queryType;
this.timestamp = timestamp;
}
public String getClientIP() { return clientIP; }
public String getDomainName() { return domainName; }
public int getQueryType() { return queryType; }
public LocalDateTime getTimestamp() { return timestamp; }
}
public static class DNSZone {
private final String name;
private final Map<String, String> records = new HashMap<>();
public DNSZone(String name) {
this.name = name;
}
public void addRecord(String domain, String ip) {
records.put(domain.toLowerCase(), ip);
}
public String resolve(String domain) {
return records.get(domain.toLowerCase());
}
public String getName() { return name; }
public Map<String, String> getRecords() { return records; }
}
public static class ClientQueryHistory {
private final String domain;
private final LocalDateTime timestamp;
public ClientQueryHistory(String domain, LocalDateTime timestamp) {
this.domain = domain;
this.timestamp = timestamp;
}
public String getDomain() { return domain; }
public LocalDateTime getTimestamp() { return timestamp; }
}
public static class DNSEvent {
private final String clientIP;
private final String domain;
private final String eventType;
private final String details;
private final LocalDateTime timestamp;
public DNSEvent(String clientIP, String domain, String eventType, String details, LocalDateTime timestamp) {
this.clientIP = clientIP;
this.domain = domain;
this.eventType = eventType;
this.details = details;
this.timestamp = timestamp;
}
public String getClientIP() { return clientIP; }
public String getDomain() { return domain; }
public String getEventType() { return eventType; }
public String getDetails() { return details; }
public LocalDateTime getTimestamp() { return timestamp; }
}
}