mirror of
https://gitee.com/willfree/min-vpn-client_v2.git
synced 2026-10-04 19:42:00 +08:00
writing vpnconnection
This commit is contained in:
1 parent
49d12bffcb
commit
87ae0d3c77
4 files changed
+295
-2
No files matched your search
@@ -19,10 +19,17 @@ import android.util.Log;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.pkusz.min_vpn_client.model.MINVpnAPI;
|
||||
import com.pkusz.min_vpn_client.utils.AESHelperForConnection;
|
||||
import com.pkusz.min_vpn_client.utils.IPPackageUtil;
|
||||
import com.pkusz.min_vpn_client.utils.KeyManager;
|
||||
import com.pkusz.min_vpn_client.utils.RSACryptoForConnection;
|
||||
import com.pkusz.min_vpn_client.utils.RuleUtil;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import common.LoggerHelper;
|
||||
@@ -31,8 +38,10 @@ import component.Identifier;
|
||||
import component.IdentifierComponent;
|
||||
import logicface.LogicFace;
|
||||
import logicface.LogicFaceException;
|
||||
import mgmt.RegisterPrefixHelper;
|
||||
import packet.Data;
|
||||
import packet.Interest;
|
||||
import security.KeyChain;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
@@ -145,16 +154,108 @@ public class MINVpnConnection implements Runnable{
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
public void run() {
|
||||
// 建立一条连接服务器的TCP-LogicFace
|
||||
// 1. 建立一条连接服务器的TCP-LogicFace
|
||||
LogicFace face=initTCPLogicFace(this.mServerName,this.mServerPort);
|
||||
if(face==null){
|
||||
LoggerHelper.severe("vpn connection run error: tcp logic face init error");
|
||||
return;
|
||||
}
|
||||
|
||||
// VPN握手,交换信息
|
||||
// 2. VPN握手,交换信息
|
||||
handShake(face);
|
||||
if(this.mParcelFileDescriptor==null){
|
||||
LoggerHelper.severe("vpn connection run error: handle shake error");
|
||||
}
|
||||
|
||||
// 3. 进行数据中转操作
|
||||
transferData(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* 双线程中转数据
|
||||
* @param face
|
||||
* @return
|
||||
*/
|
||||
private boolean transferData(LogicFace face){
|
||||
FileInputStream inputStream=new FileInputStream(
|
||||
this.mParcelFileDescriptor.getFileDescriptor());
|
||||
FileOutputStream outputStream=new FileOutputStream(
|
||||
this.mParcelFileDescriptor.getFileDescriptor());
|
||||
|
||||
// -------------- 线程1:从tun网卡读出网络包,并往服务器发 -----------------
|
||||
ByteBuffer buff1 = ByteBuffer.allocate(MAX_PACKET_SIZE);
|
||||
ByteBuffer buff2 = ByteBuffer.allocate(MAX_PACKET_SIZE);
|
||||
|
||||
Thread recvFromTunAndSendThread = new Thread(() -> {
|
||||
while (!this.stop) {
|
||||
buff1.clear();
|
||||
buff2.clear();
|
||||
int readLen;
|
||||
int encryptedLen;
|
||||
try {
|
||||
readLen = inputStream.read(buff1.array(), 0, MAX_PACKET_SIZE);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
if (readLen > 0) {
|
||||
System.out.println("read from tun , readLen = " + readLen);
|
||||
|
||||
buff1.position(0);
|
||||
buff1.limit(readLen);
|
||||
|
||||
//buff2.put(IPPackageUtil.getSrcIpAddress(buff1.array()));
|
||||
buff2.put(IPPackageUtil.StringAddress2byteAddress(allocatedAddress));
|
||||
byte[] ipPackageData = new byte[readLen];
|
||||
System.arraycopy(buff1.array(), 0, ipPackageData, 0, readLen);
|
||||
byte[] encrytedIPPackage = AESHelperForConnection.encryptWithAES(mSharedSecretString, ipPackageData);
|
||||
System.out.println("encryptedIPPackage len: " + encrytedIPPackage.length);
|
||||
System.out.println("buff2 position: " + buff2.position());
|
||||
buff2.put(encrytedIPPackage);
|
||||
encryptedLen = buff2.position();
|
||||
buff2.position(0);
|
||||
buff2.limit(encryptedLen);
|
||||
//buff2.flip();
|
||||
sendInterestPkt(face, buff2);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
recvFromTunAndSendThread.start();
|
||||
|
||||
try {
|
||||
RegisterPrefixHelper helper=new RegisterPrefixHelper();
|
||||
face.registerIdentifier(new Identifier(this.localPrefix),5000,helper);
|
||||
|
||||
Interest interest= face.receiveInterest(5000);
|
||||
|
||||
System.out.println("recv interest packet, name : " + interest.getName().toString());
|
||||
System.out.println("兴趣包中装的数据长度 : " + interest.payload.getValue().length);
|
||||
int dataLen = interest.payload.getValue().length;
|
||||
ByteBuffer buf2 = ByteBuffer.allocate(dataLen);
|
||||
buf2.put(interest.payload.getValue());
|
||||
System.out.println("buf2 data len = " + buf2.position());
|
||||
byte[] decrpytedIPPackage = new byte[buf2.position()];
|
||||
System.arraycopy(buf2.array(), 0, decrpytedIPPackage, 0, buf2.position());
|
||||
ByteBuffer buf3 = ByteBuffer.allocate(5000);
|
||||
buf3.put(Objects.requireNonNull(AESHelperForConnection.decryptWithAES(mSharedSecretString, decrpytedIPPackage)));
|
||||
int decryptedDataLen = buf3.position();
|
||||
byte firstByte = buf3.get(0);
|
||||
if(firstByte == 0) {
|
||||
//提示连接超时
|
||||
mOnTimeoutFailedListener.onTimeout();
|
||||
} else {
|
||||
try {
|
||||
outputStream.write(buf3.array(), 0, decryptedDataLen);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SecurityException | ComponentException | LogicFaceException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,10 +419,25 @@ public class MINVpnConnection implements Runnable{
|
||||
return vpnInterface;
|
||||
}
|
||||
|
||||
// 输出提示信息时使用
|
||||
private final String getTag() {
|
||||
return MINVpnConnection.class.getSimpleName() + "[" + mConnectionId + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据builder进行规则路由配置
|
||||
* @param builder
|
||||
*/
|
||||
private void configRoute(VpnService.Builder builder) {
|
||||
String[] routes = RuleUtil.getRules(mService);
|
||||
for(int i = 0; i < routes.length; i++) {
|
||||
String route = routes[i];
|
||||
String[] fields = route.split("/");
|
||||
//System.out.println(fields[0]);
|
||||
builder.addRoute(fields[0], Integer.parseInt(fields[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个连接指定服务器地址的TCP LogicFace
|
||||
* @param hostIP
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.pkusz.min_vpn_client.utils;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class AESHelperForConnection {
|
||||
// AES + Base64 encrypt
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static String encryptWithAES(String key, String message) {
|
||||
try {
|
||||
// Use md5 value as the real key
|
||||
byte[] b = key.getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] keyData = md.digest(b);
|
||||
|
||||
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
|
||||
// Create an 8-byte initialization vector
|
||||
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f };
|
||||
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
|
||||
|
||||
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
// CBC requires an initialization vector
|
||||
ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
|
||||
|
||||
byte[] plaintext = message.getBytes();
|
||||
byte[] result = ecipher.doFinal(plaintext, 0, plaintext.length);
|
||||
Base64.Encoder encoder = Base64.getEncoder();
|
||||
// return Base64.encodeToString(result, Base64.DEFAULT);
|
||||
return encoder.encodeToString(result);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// AES without base64 encrypt
|
||||
public static byte[] encryptWithAES(String key, byte[] plaintext) {
|
||||
try {
|
||||
// Use md5 value as the real key
|
||||
byte[] b = key.getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] keyData = md.digest(b);
|
||||
|
||||
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
|
||||
// Create an 8-byte initialization vector
|
||||
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f };
|
||||
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
|
||||
|
||||
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
// CBC requires an initialization vector
|
||||
ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
|
||||
return ecipher.doFinal(plaintext, 0, plaintext.length);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// base64 -> AES decrypt
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static String decryptWithAES(String key, String message) {
|
||||
try {
|
||||
// Use md5 value as the real key
|
||||
byte[] b = key.getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] keyData = md.digest(b);
|
||||
|
||||
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
|
||||
// Create an 8-byte initialization vector
|
||||
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f };
|
||||
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
|
||||
|
||||
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
// CBC requires an initialization vector
|
||||
dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
|
||||
Base64.Decoder decoder = Base64.getDecoder();
|
||||
byte[] messageData = decoder.decode(message);
|
||||
byte[] result = dcipher.doFinal(messageData, 0, messageData.length);
|
||||
// System.out.println("result = " + Arrays.toString(result));
|
||||
return new String(result);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// AES without base64
|
||||
public static byte[] decryptWithAES(String key, final byte[] message) {
|
||||
try {
|
||||
// Use md5 value as the real key
|
||||
byte[] b = key.getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] keyData = md.digest(b);
|
||||
|
||||
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
|
||||
// Create an 8-byte initialization vector
|
||||
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f };
|
||||
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
|
||||
|
||||
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
// CBC requires an initialization vector
|
||||
dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
|
||||
return dcipher.doFinal(message, 0, message.length);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.pkusz.min_vpn_client.utils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class IPPackageUtil {
|
||||
|
||||
public static byte[] getSrcIpAddress(byte[] ipPackage) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
|
||||
byteBuffer.put(ipPackage, 12, 4);
|
||||
byteBuffer.flip();
|
||||
return byteBuffer.array();
|
||||
}
|
||||
|
||||
public static byte[] StringAddress2byteAddress(String address) {
|
||||
try {
|
||||
return InetAddress.getByName(address).getAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.pkusz.min_vpn_client.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Scanner;
|
||||
|
||||
|
||||
public class RuleUtil {
|
||||
public static String[] getRules(Context context) {
|
||||
ArrayList<String> ruleArrayList = new ArrayList<>();
|
||||
try {
|
||||
InputStream ruleStream = context.getAssets().open("rulelist.txt");
|
||||
Scanner scanner = new Scanner(ruleStream, "UTF-8");
|
||||
while (scanner.hasNextLine()) {
|
||||
String rule = scanner.nextLine();
|
||||
if(rule.length() > 0) {
|
||||
ruleArrayList.add(rule);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String[] rules = (String[]) ruleArrayList.toArray(new String[ruleArrayList.size()]);
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user