forked from KNEMC/KLALB
274 lines
7.0 KiB
Java
274 lines
7.0 KiB
Java
package org.kne.cloud.network.frpc;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.io.PrintStream;
|
|
import java.io.PrintWriter;
|
|
import java.lang.ProcessHandle.Info;
|
|
import java.net.BindException;
|
|
import java.net.ConnectException;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.util.stream.Stream;
|
|
|
|
public class FrpcProcess extends Process{
|
|
private static File defaultBase=new File("frpc");
|
|
|
|
private volatile Thread hook;
|
|
|
|
public static File getDefaultBase() {
|
|
return defaultBase;
|
|
}
|
|
public static void setDefaultBase(File defaultBase) {
|
|
FrpcProcess.defaultBase = defaultBase;
|
|
}
|
|
private File base;
|
|
private File exe;
|
|
private Process theProcess;
|
|
|
|
private File config_file;
|
|
|
|
public FrpcProcess(String ini) throws IOException {
|
|
this(defaultBase,ini);
|
|
|
|
}
|
|
public FrpcProcess(File base,String ini) throws IOException {
|
|
this.base=base;
|
|
if(System.getProperty("os.name").toLowerCase().contains("windows")) {
|
|
exe=new File(base,"frpc_windows_amd64.exe");
|
|
}else {
|
|
exe=new File(base,"frpc_linux_amd64");
|
|
}
|
|
AtomicReference<IOException >arex=new AtomicReference<IOException >();
|
|
synchronized (getClass()) {
|
|
UUID uid=UUID.randomUUID();
|
|
config_file = new File(base,"frpc_conf_"+uid+".ini");
|
|
config_file.createNewFile();
|
|
config_file.deleteOnExit();
|
|
PrintWriter ps=null;
|
|
try {
|
|
ps=new PrintWriter(config_file);
|
|
ps.println(ini);
|
|
}finally {
|
|
if(ps!=null)
|
|
ps.close();
|
|
}
|
|
List<String>lstc=new ArrayList<>();
|
|
lstc.add(exe.getAbsolutePath());
|
|
lstc.add("-c");
|
|
lstc.add(config_file.getName());
|
|
ProcessBuilder psb=new ProcessBuilder(lstc);
|
|
psb.directory(base);
|
|
theProcess=psb.start();
|
|
Thread t=Thread.currentThread();
|
|
|
|
hook=new Thread(()->{
|
|
try {
|
|
hook=null;
|
|
finalize();
|
|
} catch (Throwable e) {
|
|
e.printStackTrace();
|
|
}
|
|
});
|
|
Runtime.getRuntime().addShutdownHook(hook);
|
|
|
|
CountDownLatch cdl=new CountDownLatch(1);
|
|
new Thread(()->{
|
|
BufferedReader br=new BufferedReader(new InputStreamReader(theProcess.getInputStream()));
|
|
|
|
try {
|
|
while(true) {
|
|
String s=br.readLine();
|
|
if(s==null) {
|
|
break;
|
|
}
|
|
cdl.countDown();
|
|
System.out.println ("{"+uid+"}"+s);
|
|
if(s.contains("start proxy success")) {
|
|
t.interrupt();
|
|
}else if(s.contains("port already used")){
|
|
arex.set(new BindException("port on server already used"));
|
|
t.interrupt();
|
|
}else if(s.contains("远程端口已经在使用了")) {
|
|
arex.set(new BindException("port on server already used"));
|
|
t.interrupt();
|
|
}else if(s.contains("no such host")) {
|
|
arex.set(new UnknownHostException("no such host"));
|
|
t.interrupt();
|
|
}else if(s.contains("connection write timeout")) {
|
|
arex.set(new ConnectException("connection write timeout"));
|
|
t.interrupt();
|
|
}else if(s.contains("already exists")&&s.contains("proxy")) {
|
|
arex.set(new BindException("proxy already exists"));
|
|
t.interrupt();
|
|
}else if(s.contains("已经存在, 请检查是否重复开启")&&s.contains("隧道")) {
|
|
arex.set(new BindException("proxy already exists"));
|
|
t.interrupt();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}finally {
|
|
try {
|
|
br.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
}).start();
|
|
try {
|
|
cdl.await(5000, TimeUnit.MILLISECONDS);
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
try {
|
|
if(theProcess.waitFor(20000, TimeUnit.MILLISECONDS)) {
|
|
destroy();
|
|
throw new SocketException("Frpc exit code:"+theProcess.exitValue());
|
|
}else {
|
|
destroy();
|
|
throw new SocketException("Frpc timed out");
|
|
}
|
|
} catch (InterruptedException e) {
|
|
//destroy();
|
|
Thread.interrupted();
|
|
if(arex.get()!=null) {
|
|
destroy();
|
|
arex.get().fillInStackTrace();
|
|
throw arex.get();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
public File getBase() {
|
|
return base;
|
|
}
|
|
@Override
|
|
public OutputStream getOutputStream() {
|
|
return theProcess.getOutputStream();
|
|
}
|
|
@Override
|
|
public InputStream getInputStream() {
|
|
return theProcess.getInputStream();
|
|
}
|
|
@Override
|
|
public InputStream getErrorStream() {
|
|
return theProcess.getErrorStream();
|
|
}
|
|
@Override
|
|
public int waitFor() throws InterruptedException {
|
|
return theProcess.waitFor();
|
|
}
|
|
@Override
|
|
public int exitValue() {
|
|
return theProcess.exitValue();
|
|
}
|
|
@Override
|
|
public void destroy() {
|
|
theProcess.destroy();
|
|
//System.out.println("frpc已关闭");
|
|
if(hook!=null) {
|
|
Runtime.getRuntime().removeShutdownHook(hook);
|
|
}
|
|
if(config_file!=null) {
|
|
config_file.delete();
|
|
}
|
|
}
|
|
@Override
|
|
public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException {
|
|
return theProcess.waitFor(timeout, unit);
|
|
}
|
|
@Override
|
|
public Process destroyForcibly() {
|
|
Process b= theProcess.destroyForcibly();
|
|
//System.out.println("frpc已关闭");
|
|
if(hook!=null) {
|
|
Runtime.getRuntime().removeShutdownHook(hook);
|
|
}
|
|
return b;
|
|
}
|
|
@Override
|
|
public boolean supportsNormalTermination() {
|
|
return theProcess.supportsNormalTermination();
|
|
}
|
|
@Override
|
|
public boolean isAlive() {
|
|
return theProcess.isAlive();
|
|
}
|
|
@Override
|
|
public long pid() {
|
|
return theProcess.pid();
|
|
}
|
|
@Override
|
|
public CompletableFuture<Process> onExit() {
|
|
return theProcess.onExit();
|
|
}
|
|
@Override
|
|
public ProcessHandle toHandle() {
|
|
return theProcess.toHandle();
|
|
}
|
|
@Override
|
|
public Info info() {
|
|
return theProcess.info();
|
|
}
|
|
@Override
|
|
public Stream<ProcessHandle> children() {
|
|
return theProcess.children();
|
|
}
|
|
@Override
|
|
public Stream<ProcessHandle> descendants() {
|
|
return theProcess.descendants();
|
|
}
|
|
public static void main(String[] args) throws IOException {
|
|
String str="""
|
|
[common]
|
|
server_addr = us.afrps.cn
|
|
server_port = 7000
|
|
token = afrps.cn
|
|
protocol=kcp
|
|
[test5]
|
|
type = tcp
|
|
local_ip = 127.0.0.1
|
|
local_port = 7461
|
|
remote_port = 49966
|
|
use_encryption = true
|
|
use_compression = true
|
|
""";
|
|
FrpcIni fi=new FrpcIni(str);
|
|
|
|
str=fi.storeToString();
|
|
//System.out.println(fi.getServerPort());
|
|
FrpcProcess fp=new FrpcProcess(str);
|
|
try {
|
|
Thread.sleep(10000);
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
@Override
|
|
protected void finalize() throws Throwable {
|
|
|
|
if(theProcess!=null) {
|
|
destroy();
|
|
}
|
|
}
|
|
}
|