移除加密代理;新增备用订阅更新。
This commit is contained in:
parent
95329a5603
commit
ceef2dae12
7
.idea/encodings.xml
Normal file
7
.idea/encodings.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
pom.xml
6
pom.xml
@ -58,6 +58,12 @@
|
|||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.25.0</version>
|
<version>1.25.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.5.14</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
23
src/main/java/lion/Config/Config.java
Normal file
23
src/main/java/lion/Config/Config.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package lion.Config;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
public static String DouNaiV2ray;
|
||||||
|
public static String DouNaiClash;
|
||||||
|
|
||||||
|
public static void loadConfig(){
|
||||||
|
Properties prop = new Properties();
|
||||||
|
|
||||||
|
try (InputStream input = new FileInputStream("/root/gallery/storageNode/config.properties")) {
|
||||||
|
prop.load(input);
|
||||||
|
DouNaiV2ray = prop.getProperty("DouNaiV2ray");
|
||||||
|
DouNaiClash = prop.getProperty("DouNaiClash");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
295
src/main/java/lion/Externel/BackupSubServer.java
Normal file
295
src/main/java/lion/Externel/BackupSubServer.java
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
package lion.Externel;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static lion.Config.Config.DouNaiClash;
|
||||||
|
import static lion.Config.Config.DouNaiV2ray;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BackupSubServer {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
updateSub();
|
||||||
|
ExecutorService threadPool = Executors.newFixedThreadPool(3600);
|
||||||
|
threadPool.submit(() -> {
|
||||||
|
if(LocalDateTime.now().getHour() == 0){
|
||||||
|
updateSub();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String ip = "";
|
||||||
|
try(ServerSocket serverSocket = new ServerSocket(8889)) {
|
||||||
|
log.info("Sub Server listening on port {}", 8889);
|
||||||
|
while (true) {
|
||||||
|
Socket clientSocket = serverSocket.accept();
|
||||||
|
ip = clientSocket.getInetAddress().getHostAddress();
|
||||||
|
log.info("Client connected:{}", ip);
|
||||||
|
// 线程池处理下载请求
|
||||||
|
handleClientRequest(clientSocket);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("处理http请求时出错,IP:{},ERROR:{}", ip, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateSub(){
|
||||||
|
File DouNaiClashFile = new File("sub/DouNaiClash.txt");
|
||||||
|
File DouNaiV2rayFile = new File("sub/DouNaiV2ray.txt");
|
||||||
|
File directory = new File("sub");
|
||||||
|
|
||||||
|
if(!directory.isDirectory())
|
||||||
|
try {
|
||||||
|
Files.createDirectory(Paths.get("sub"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("create directory error:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> DouNaiClash_profile;
|
||||||
|
|
||||||
|
//下载豆奶v2ray订阅
|
||||||
|
try(FileWriter writer = new FileWriter(DouNaiV2rayFile)) {
|
||||||
|
String DouNaiV2rayRaw = Get(DouNaiV2ray).getFirst();
|
||||||
|
String[] v2rayPlain = new String(Base64.getDecoder().decode(DouNaiV2rayRaw)).split("\n");
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?");
|
||||||
|
|
||||||
|
//过滤高倍率节点
|
||||||
|
for(String node: v2rayPlain){
|
||||||
|
String name = URLDecoder.decode(node.split("#")[1], StandardCharsets.UTF_8);
|
||||||
|
if(name.contains("流量")){
|
||||||
|
Matcher matcher = pattern.matcher(name.substring(name.indexOf("(") + 1, name.indexOf(")")));
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
// 将匹配到的数字添加到列表中
|
||||||
|
float ratio = Float.parseFloat(matcher.group());
|
||||||
|
if(ratio <= 2) {
|
||||||
|
stringBuilder.append(node).append("\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.append(node).append("\n");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
stringBuilder.append(node).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.write(new String(Base64.getEncoder().encode(stringBuilder.toString().getBytes(StandardCharsets.UTF_8))));
|
||||||
|
|
||||||
|
log.info("load DouNai v2ray complete");
|
||||||
|
}catch (IOException e){
|
||||||
|
log.error("load DouNai v2ray failure: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
//下载豆奶clash订阅
|
||||||
|
try(FileWriter writer = new FileWriter(DouNaiClashFile)) {
|
||||||
|
DouNaiClash_profile = Get(DouNaiClash);
|
||||||
|
//过滤高倍率节点
|
||||||
|
ArrayList<String> clashProcessed = new ArrayList<>();
|
||||||
|
boolean isProxies = false;
|
||||||
|
boolean skip = false;
|
||||||
|
for(String line: DouNaiClash_profile){
|
||||||
|
if(line.equals("proxies:"))
|
||||||
|
isProxies = true;
|
||||||
|
else if(line.equals("proxy-groups:") && isProxies)
|
||||||
|
isProxies = false;
|
||||||
|
|
||||||
|
if(isProxies) {
|
||||||
|
if (line.contains("name"))
|
||||||
|
skip = line.contains("流量");
|
||||||
|
if (!skip)
|
||||||
|
clashProcessed.add(line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (!line.contains("流量"))
|
||||||
|
clashProcessed.add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String line: clashProcessed)
|
||||||
|
writer.write(line + "\n");
|
||||||
|
|
||||||
|
log.info("load DouNai clash complete");
|
||||||
|
}catch (IOException e){
|
||||||
|
log.error("load DouNai clash failure: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ArrayList<String> Get(String url) throws IOException {
|
||||||
|
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
CloseableHttpResponse httpResponse;
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
|
||||||
|
httpResponse = httpClient.execute(httpGet);
|
||||||
|
|
||||||
|
HttpEntity responseEntity = httpResponse.getEntity();
|
||||||
|
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||||
|
ArrayList<String> temp = new ArrayList<>();
|
||||||
|
|
||||||
|
if (statusCode == 200) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
|
||||||
|
String str;
|
||||||
|
while ((str = reader.readLine()) != null)
|
||||||
|
temp.add(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.close();
|
||||||
|
httpResponse.close();
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleClientRequest(Socket clientSocket) {
|
||||||
|
String fileName = "";
|
||||||
|
try {
|
||||||
|
BufferedReader requestReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||||
|
String requestLine = requestReader.readLine();
|
||||||
|
|
||||||
|
// Parse the request line to get the method and path
|
||||||
|
String[] requestParts = requestLine.split(" ");
|
||||||
|
String method = requestParts[0];
|
||||||
|
Map<String, String> paramMap = parseRequestLine(requestParts[1]);//path
|
||||||
|
if(paramMap == null){
|
||||||
|
sendErrorResponse(clientSocket, "404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info(Arrays.toString(requestParts));
|
||||||
|
|
||||||
|
// Only handle GET requests
|
||||||
|
if (method.equals("GET")) {
|
||||||
|
// Set the file path for download
|
||||||
|
File file = new File(fileName);
|
||||||
|
switch (paramMap.get("Client")) {
|
||||||
|
case "v2" -> file = new File("sub/DouNaiV2ray.txt");
|
||||||
|
case "cat" -> file = new File("sub/DouNaiClash.txt");
|
||||||
|
}
|
||||||
|
fileName = file.getName();
|
||||||
|
log.info(file.getAbsolutePath());
|
||||||
|
// Check if the file exists and is readable
|
||||||
|
if (file.exists() && file.isFile() && file.canRead()) {
|
||||||
|
// Get the file length
|
||||||
|
long fileLength = file.length();
|
||||||
|
|
||||||
|
// Get the range information for resuming download
|
||||||
|
long startByte = 0;
|
||||||
|
long endByte = fileLength - 1;
|
||||||
|
String rangeHeader = getRequestHeader(requestReader);
|
||||||
|
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
|
||||||
|
String[] rangeValues = rangeHeader.substring(6).split("-");
|
||||||
|
startByte = Long.parseLong(rangeValues[0]);
|
||||||
|
if (rangeValues.length > 1 && !rangeValues[1].isEmpty()) {
|
||||||
|
endByte = Long.parseLong(rangeValues[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the HTTP response headers
|
||||||
|
OutputStream responseStream = clientSocket.getOutputStream();
|
||||||
|
PrintWriter responseWriter = new PrintWriter(responseStream, true);
|
||||||
|
responseWriter.println("HTTP/1.1 206 Partial Content");
|
||||||
|
responseWriter.println("Content-Type: application/octet-stream");
|
||||||
|
responseWriter.println("Accept-Ranges: bytes");
|
||||||
|
responseWriter.println("Content-Length: " + (endByte - startByte + 1));
|
||||||
|
responseWriter.println("Content-Range: bytes " + startByte + "-" + endByte + "/" + fileLength);
|
||||||
|
responseWriter.println();
|
||||||
|
|
||||||
|
// Send the file content
|
||||||
|
try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
|
||||||
|
randomAccessFile.seek(startByte);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
long bytesRemaining = endByte - startByte + 1;
|
||||||
|
while (bytesRemaining > 0 && (bytesRead = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, bytesRemaining))) != -1) {
|
||||||
|
responseStream.write(buffer, 0, bytesRead);
|
||||||
|
bytesRemaining -= bytesRead;
|
||||||
|
}
|
||||||
|
}catch (SocketException ignore){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the response output stream
|
||||||
|
responseStream.close();
|
||||||
|
} else {
|
||||||
|
// File not found or not readable, send 404 response
|
||||||
|
sendErrorResponse(clientSocket, "404 Not Found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-GET requests, send 501 response
|
||||||
|
sendErrorResponse(clientSocket, "501 Not Implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the request reader and client socket
|
||||||
|
requestReader.close();
|
||||||
|
clientSocket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("处理文件下载时出错,IP:{}, 文件:{}, ERROR:{}", clientSocket.getInetAddress().getHostAddress(), fileName, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getRequestHeader(BufferedReader requestReader) throws IOException {
|
||||||
|
String line;
|
||||||
|
while ((line = requestReader.readLine()) != null) {
|
||||||
|
if (line.trim().isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.startsWith("Range" + ":")) {
|
||||||
|
return line.substring("Range".length() + 1).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> parseRequestLine(String requestLine) {
|
||||||
|
Map<String, String> pathParams = new HashMap<>();
|
||||||
|
|
||||||
|
if(requestLine == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String path;
|
||||||
|
if(requestLine.contains("?"))
|
||||||
|
path = requestLine.split("\\?")[0];
|
||||||
|
else
|
||||||
|
path = requestLine;
|
||||||
|
|
||||||
|
String[] vars = path.split("/");
|
||||||
|
|
||||||
|
if(vars.length < 4)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
pathParams.put("Key", vars[3]);
|
||||||
|
pathParams.put("Client", vars[2]);
|
||||||
|
|
||||||
|
if(!pathParams.get("Client").equals("cat") && !pathParams.get("Client").equals("v2"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if(pathParams.get("Key").length()<6)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return pathParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendErrorResponse(Socket clientSocket, String statusCode) throws IOException {
|
||||||
|
OutputStream responseStream = clientSocket.getOutputStream();
|
||||||
|
PrintWriter responseWriter = new PrintWriter(responseStream, true);
|
||||||
|
responseWriter.println("HTTP/1.1 " + statusCode);
|
||||||
|
responseWriter.println("Content-Type: text/html");
|
||||||
|
responseWriter.println();
|
||||||
|
responseWriter.println("<h1>" + statusCode + "</h1>");
|
||||||
|
responseStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package lion.Extranel;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.security.Key;
|
|
||||||
|
|
||||||
public class AESUtils {
|
|
||||||
private static final String ALGORITHM = "AES";
|
|
||||||
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
|
||||||
|
|
||||||
private static final byte[] keyBytes = "ThisIsA128BitKey".getBytes();
|
|
||||||
|
|
||||||
public static byte[] encrypt(byte[] data) throws Exception {
|
|
||||||
Key key = new SecretKeySpec(keyBytes, ALGORITHM);
|
|
||||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
||||||
return cipher.doFinal(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] decrypt(byte[] encryptedData) throws Exception {
|
|
||||||
Key key = new SecretKeySpec(keyBytes, ALGORITHM);
|
|
||||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
|
||||||
return cipher.doFinal(encryptedData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package lion.Extranel;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lion.CustomUtil;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class Server {
|
|
||||||
|
|
||||||
ExecutorService thread_pool;
|
|
||||||
|
|
||||||
ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
new Server();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Server(){
|
|
||||||
log.info("开始监听:55555");
|
|
||||||
objectMapper = CustomUtil.objectMapper;
|
|
||||||
thread_pool = Executors.newFixedThreadPool(2);
|
|
||||||
thread_pool.submit(() -> {
|
|
||||||
try(ServerSocket server = new ServerSocket(55555)){
|
|
||||||
server.setSoTimeout(1000000);
|
|
||||||
while (true){
|
|
||||||
Socket socket = server.accept();
|
|
||||||
thread_pool.submit(() -> handleSocket(socket));
|
|
||||||
}
|
|
||||||
}catch (IOException e){
|
|
||||||
log.error(e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void handleSocket(Socket socket){
|
|
||||||
try{
|
|
||||||
BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream());
|
|
||||||
Thread.sleep(500);
|
|
||||||
byte[] buf = new byte[inputStream.available()];
|
|
||||||
inputStream.read(buf);
|
|
||||||
byte[] bytes = AESUtils.decrypt(buf);
|
|
||||||
|
|
||||||
HashMap<String, String> map = (HashMap<String, String>) objectMapper.readValue(bytes, HashMap.class);
|
|
||||||
URL url = new URI(map.get("path")).toURL();
|
|
||||||
log.info("处理反代 ip: {},路径: {}", socket.getInetAddress().getHostAddress(), map.get("path"));
|
|
||||||
bytes = url.openConnection().getInputStream().readAllBytes();
|
|
||||||
socket.getOutputStream().write(AESUtils.encrypt(bytes));
|
|
||||||
socket.getOutputStream().flush();
|
|
||||||
socket.getOutputStream().close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,8 @@
|
|||||||
package lion;
|
package lion;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import lion.Extranel.Server;
|
import lion.Config.Config;
|
||||||
|
import lion.Externel.BackupSubServer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -9,8 +10,9 @@ public class Main {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
boot();
|
boot();
|
||||||
|
Config.loadConfig();
|
||||||
|
new Thread(() -> BackupSubServer.main(null)).start();
|
||||||
new Thread(() -> MultiThreadedHTTPServer.main(null)).start();
|
new Thread(() -> MultiThreadedHTTPServer.main(null)).start();
|
||||||
new Thread(() -> Server.main(null)).start();
|
|
||||||
new storageNode();
|
new storageNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
src/main/resources/config.properties
Normal file
2
src/main/resources/config.properties
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DouNaiV2ray=https://aaaa.gay/link/X7zEqkIx5gtIGugO?client=v2
|
||||||
|
DouNaiClash=https://aaaa.gay/link/X7zEqkIx5gtIGugO?client=clashmeta
|
||||||
Loading…
Reference in New Issue
Block a user