YanFrp/src/main/java/top/octopusyan/manager/FrpManager.java

332 lines
12 KiB
Java

package top.octopusyan.manager;
import javafx.application.Platform;
import javafx.scene.control.TextArea;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import top.octopusyan.http.EasyHttp;
import top.octopusyan.http.model.ResponseClass;
import top.octopusyan.manager.http.Api;
import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.model.ProxySetupModel;
import top.octopusyan.utils.EncryptionUtil;
import top.octopusyan.utils.JsoupUtil;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author : octopus yan
* @email : octopus_yan@foxmail.com
* @description : frp 客户端 工具
* @create : 2022-4-7 23:19
*/
public class FrpManager {
public static final String serverConfigHeader = "yanfrp://";
public static List<FrpManager> frpcList = new ArrayList<>();
private static final String YAN_FRP_TEMP_DIR_NAME = "frpclienttmp";
public static final String FRPC_CLIENT_FILE_NAME = "frpclienttmpfile.exe";
private static final String FRPC_CONF_PREFIX_NAME = "proxy_";
private static final String FRPC_CONF_SUFFIX_NAME = ".ini";
private static final Map<Integer, String> serverConfigraution = new HashMap<>(3);
/**
* 应用临时目录 地址
*/
private static final String YAN_FRP_TEMP_DIR_PATH = System.getProperty("java.io.tmpdir") + System.currentTimeMillis() + YAN_FRP_TEMP_DIR_NAME;
/**
* frpc 地址
*/
private static final String FRPC_CLIENT_FILE_PATH = ApplicatonStore.appDataDirPath + File.separator + FRPC_CLIENT_FILE_NAME;
private static final File frpconfigDir = new File(YAN_FRP_TEMP_DIR_PATH);
public static final File frpc = new File(FRPC_CLIENT_FILE_PATH);
public File frpcConfigFile;
private final ProxySetupModel model;
private final TextArea console;
private ProxyThread thread;
private Process exec;
private FrpManager(TextArea console, ProxySetupModel model) {
this.console = console;
this.model = model;
}
public static FrpManager init(ProxySetupModel model) {
TextArea console = new TextArea();
console.setPrefHeight(180);
console.setMinWidth(200);
FrpManager frpManager = new FrpManager(console, model);
frpcList.add(frpManager);
return frpManager;
}
public TextArea getConsole() {
return console;
}
/**
* 开始
*/
public void start() {
thread = new ProxyThread();
thread.setName(FRPC_CONF_PREFIX_NAME + model.getSort());
thread.start();
}
/**
* 停止隧道
*/
public void stop() {
ThreadPoolManager.getInstance().execute(() -> {
if (thread != null) {
do {
if (exec != null && exec.isAlive())
exec.destroy();
thread.setExit(true);
thread.interrupt();
} while (!thread.isInterrupted());
Platform.runLater(() -> {
model.setRunning(false);
console.appendText("yan-frp-info:已停止\n");
});
// 尝试删除缓存配置文件
if (frpcConfigFile.exists()) frpcConfigFile.delete();
}
});
}
/**
* 清理日志
*/
public void clearLog() {
if (console != null)
Platform.runLater(console::clear);
}
/**
* 隧道配置文件地址
*/
private String getConfigFilePath() {
return YAN_FRP_TEMP_DIR_PATH + File.separator + FRPC_CONF_PREFIX_NAME + model.getSort() + FRPC_CONF_SUFFIX_NAME;
}
/**
* 初始化隧道配置文件
*/
private void initProxyConfigFile() throws IOException {
// 创建配置文件
frpcConfigFile = new File(getConfigFilePath());
if (!frpcConfigFile.exists()) {
FileUtils.createParentDirectories(frpcConfigFile);
frpcConfigFile.createNewFile();
}
frpcConfigFile.deleteOnExit();
// 写入服务配置
FileUtils.write(frpcConfigFile, getUserFrpServerConfig(model.get().getNode()), StandardCharsets.UTF_8);
// 写入隧道配置
FileUtils.write(frpcConfigFile, getProxyFrpConfig(model), StandardCharsets.UTF_8, true);
}
public static String getUserFrpServerConfig(int node) {
String config = serverConfigraution.get(node);
if (StringUtils.isNotEmpty(config)) return config;
try {
String result = EasyHttp.builder()
.api(Api.getServerConfiguration(node))
.pathParam(String.valueOf(node))
.execute(new ResponseClass<String>() {
});
config = JsoupUtil.getServerConfiguration(result);
if (StringUtils.isNotEmpty(config)) serverConfigraution.put(node, config);
return config;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String getProxyFrpConfig(ProxySetupModel setup) {
String n = "\n";
boolean ishttp = setup.getProxyType().contains("http");
boolean isp2p = "xtcp".equals(setup.getProxyType()) || "stcp".equals(setup.getProxyType());
// 基础配置
StringBuilder stringBuilder = new StringBuilder("[");
// 服务名称
StringBuilder serverName = new StringBuilder();
// p2p 服务名
if (isp2p) {
if (setup.isProvider()) {
// 提供者
serverName.append(EncryptionUtil.MD5_16(serverName.toString()));
} else {
// 访问者
serverName.append(setup.getServerName()).append("_visitor");
}
} else {
serverName.append(ApplicatonStore.getAccount()).append("_").append(setup.getSort());
}
stringBuilder.append(serverName).append("]\n");
stringBuilder.append("privilege_mode = true\n")
.append("type = ").append(setup.getProxyType().contains("http") ? "http" : setup.getProxyType()).append(n);
if (ishttp) {
// HTTP / HTTPS
stringBuilder.append("custom_domains = ").append(setup.getDomain()).append(setup.getDomainSuffix()).append(n)
.append("local_ip = ").append(setup.getLocalIp()).append(n)
.append("local_port = ").append(setup.getLocalPort()).append(n).append(n);
if (StringUtils.isNotEmpty(setup.getLocations()))
stringBuilder.append("locations = ").append(setup.getLocations()).append(n);
if (StringUtils.isNotEmpty(setup.getHttpUser()))
stringBuilder.append("http_user = ").append(setup.getHttpUser()).append(n);
if (StringUtils.isNotEmpty(setup.getHttpPwd()))
stringBuilder.append("http_pwd = ").append(setup.getHttpPwd()).append(n);
if (StringUtils.isNotEmpty(setup.getHostHeaderRewrite()))
stringBuilder.append("host_header_rewrite = ").append(setup.getHostHeaderRewrite()).append(n);
if (StringUtils.isNotEmpty(setup.getHeader_X_From_Where()))
stringBuilder.append("header_X-From-Where = ").append(setup.getHeader_X_From_Where()).append(n);
} else if (isp2p) {
// xtcp / stcp
if (!setup.isProvider()) {
stringBuilder.append("role = visitor").append(n);
stringBuilder.append("server_name = ").append(setup.getServerName()).append(n);
stringBuilder.append("bind_addr = ").append(setup.getLocalIp()).append(n);
stringBuilder.append("bind_port = ").append(setup.getLocalPort()).append(n);
} else {
stringBuilder.append("local_ip = ").append(setup.getLocalIp()).append(n);
stringBuilder.append("local_port = ").append(setup.getLocalPort()).append(n);
}
stringBuilder.append("sk = ").append(setup.getSk()).append(n);
} else {
// TCP / UDP
stringBuilder.append("local_ip = ").append(setup.getLocalIp()).append(n)
.append("local_port = ").append(setup.getLocalPort()).append(n).append(n)
.append("remote_port = ").append(setup.getRemotePort()).append(n);
}
// 压缩和加密
stringBuilder.append("use_encryption = ").append(setup.isUseEncryption()).append(n)
.append("use_compression = ").append(setup.isUseCompression()).append(n).append(n);
return stringBuilder.toString();
}
/**
* 初始化frpc客户端文件
*/
public static void initFrpc() throws IOException {
// frpc 文件
if (!frpc.exists()) {
frpc.createNewFile();
// 复制 frpc
copyFileUsingFileStreams(Objects.requireNonNull(FrpManager.class.getResourceAsStream("/static/frpc.exe")));
}
// frpc 临时配置文件
// 存在就删除
if (frpconfigDir.exists()) FrpManager.clearTmp();
// 重新创建
frpconfigDir.mkdir();
// 发送删除请求
frpconfigDir.deleteOnExit();
}
/**
* 清理缓存
*/
public static void clearTmp() {
try {
FileUtils.deleteDirectory(frpconfigDir);
for (File file : Objects.requireNonNull(FileUtils.getTempDirectory().listFiles())) {
if (file.getPath().contains(YAN_FRP_TEMP_DIR_NAME) && file.canWrite() && file.isDirectory()) {
FileUtils.deleteDirectory(file);
}
}
} catch (IOException e) {
e.printStackTrace();
try {
Runtime.getRuntime().exec("taskkill /im " + FRPC_CLIENT_FILE_NAME + " /f");
Thread.sleep(1000);
FileUtils.deleteDirectory(frpconfigDir);
} catch (InterruptedException | IOException ex) {
ex.printStackTrace();
}
}
}
private static void copyFileUsingFileStreams(InputStream input)
throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(FrpManager.frpc);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
output.write(buf, 0, bytesRead);
}
} finally {
input.close();
if (output != null) output.close();
}
}
@EqualsAndHashCode(callSuper = true)
@Data
class ProxyThread extends Thread {
private volatile boolean exit = false;
@Override
public void run() {
Platform.runLater(() -> console.appendText("yan-frp-info:正在启动\n"));
try {
// 检查客户端文件
if (!frpc.exists()) initFrpc();
// 初始化配置文件
initProxyConfigFile();
// 执行命令
String command = FRPC_CLIENT_FILE_PATH + " -c " + getConfigFilePath();
System.out.println(command);
exec = Runtime.getRuntime().exec(command, null, frpconfigDir);
// 设置运行状态
Platform.runLater(() -> model.setRunning(true));
// 获取标准输出
BufferedReader readStdout = new BufferedReader(new InputStreamReader(exec.getInputStream()));
// 逐行读取
String line;
while ((line = readStdout.readLine()) != null && !exit) {
String finalLine = line;
System.out.println(line);
Platform.runLater(() -> console.appendText(finalLine + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
// TODO 报错
Platform.runLater(() -> console.appendText("yan-frp-error:" + e.getMessage() + "\n"));
Platform.runLater(() -> console.appendText("yan-frp-error:启动失败\n"));
}
}
}
}