332 lines
12 KiB
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"));
|
|
}
|
|
}
|
|
}
|
|
}
|