我正在使用下面的源通过 SSH 连接到服务器。程序没有任何错误。
手机屏幕顶部将出现钥匙图标:
但是我无法访问互联网:
我用其他软件测试过,例如napsternetV和NetMod(SSH),问题不在于VPS。为什么无法连接互联网?
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.VpnService;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startVpnButton = findViewById(R.id.start_vpn_button);
startVpnButton.setOnClickListener(v -> {
Intent intent = VpnService.prepare(getApplicationContext());
if (intent != null) {
startActivityForResult(intent, 0);
} else {
onActivityResult(0, RESULT_OK, null);
}
});
Button stopVpnButton = findViewById(R.id.stop_vpn_button);
stopVpnButton.setOnClickListener(v -> {
Intent intent = new Intent(getApplicationContext(), MyVpnService.class);
stopService(intent);
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Intent intent = new Intent(getApplicationContext(), MyVpnService.class);
startService(intent);
}
}
}
package com.example.myapplication;
import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import android.system.OsConstants;
import android.util.Log;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import java.io.IOException;
public class MyVpnService extends VpnService {
private ParcelFileDescriptor vpnInterface;
private Session sshSession;
// Implement onCreate, onStartCommand, and onDestroy methods
@Override
public void onCreate() {
super.onCreate();
// Initialize JSch and set up the SSH connection
JSch jsch = new JSch();
try {
sshSession = jsch.getSession("SERVER_USERNAME", "SERVER_IP", SERVER_PORT);
} catch (JSchException e) {
throw new RuntimeException(e);
}
sshSession.setPassword("SERVER_PASSWORD");
sshSession.setConfig("StrictHostKeyChecking", "no");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread vpnThread = new Thread(() -> {
try {
sshSession.connect();
int port_forwarded = sshSession.setPortForwardingL(0, "localhost", 1080);
// Configure the VPN connection
VpnService.Builder builder = new VpnService.Builder();
builder.setSession("MyVpnService")
.addAddress("10.0.0.1", 24)
.addRoute("0.0.0.0", 0)
.addDnsServer("8.8.8.8")
.setBlocking(true)
.establish();
vpnInterface = builder.establish();
// Add this line to route all traffic through the VPN
builder.allowFamily(OsConstants.AF_INET);
} catch (Exception e) {
e.printStackTrace();
}
});
vpnThread.start();
return START_STICKY;
}
@Override
public void onDestroy() {
if (vpnInterface != null) {
try {
vpnInterface.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sshSession != null) {
sshSession.disconnect();
}
super.onDestroy();
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BIND_VPN_SERVICE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
</application>
</manifest>
日志中没有任何错误,程序运行完美。我什至输入了错误的服务器信息,程序无法连接到服务器。意思是代码运行正常,但是不知道为什么无法连接互联网。
您提到的应用程序使用由 SSH 的动态端口转发创建的 SOCKS 代理。 (
ssh -D
)
目前,您的应用程序不处理任何由VPN接口拦截的数据包。
要实现此目的,您需要能够将 VPN 接口拦截的数据包转发到 SOCKS 代理,并从该 SOCKS 代理接收数据包并将其转发到 VPN 接口。
这项任务并不容易,因为 VPN 和 SOCKS 代理位于 OSI 模型的不同层。但幸运的是,已经有一个名为 Tun2Socks 的开源库可以将您的 VPN 接口连接到 SOCKS 代理。 您需要 JNI 才能使用它,因为它是用 C 编写的。
此外,JSch 库不提供对动态端口转发的支持。 JSch 库的替代方案是 Apache MINA-sshd,它提供对各种 SSH 选项的支持,包括动态端口转发。