VpnExtensionAbility的创建与系统服务查询

Ability管理 / 9人浏览

深夜,那串哈希值让我决定自己写个VPN

凌晨两点,咖啡馆的灯光昏黄得像隔了一层雾。我盯着屏幕上的交易记录,手指悬在键盘上方,迟迟没有落下。

事情还要从三天前说起。

我在一个去中心化交易所做了一笔跨链交易,把ETH换成SOL,再通过一个跨链桥转到Avalanche上。理论上,这套流程应该在三分钟内完成。但三十分钟过去了,我打开区块浏览器,发现那笔交易在一个中间地址卡住了——显示“待确认”,状态已经挂了整整四十分钟。

我立刻打开Telegram,找到那个跨链桥项目的官方群。群里一片混乱,有人骂项目方跑路,有人说合约被攻击了,还有人贴出了自己资金被卡住的截图。我的那笔交易,价值将近两个比特币。

我试着用公共RPC节点重新广播交易,提示“nonce too low”。换了个节点,提示“connection timed out”。再换一个,直接返回“你的IP被限制”。

那一刻我明白了:我的IP已经被某些节点标记了。不是因为我做了什么坏事,而是因为那个跨链桥的合约出了问题,大量交易被回滚,节点运营商为了自保,直接封杀了所有跟这个合约交互的IP段。

我翻墙出去,试图用VPN解决问题。但打开常用的那几款,发现它们也连不上那些节点——因为VPN的出口IP,早就被各大节点服务商拉黑了。

我需要一个自己的VPN。一个能让我自定义出口IP、能绕过所有黑名单、能直接连到我自己部署的节点的VPN。

于是我想到了OpenHarmony的VpnExtensionAbility。

为什么是VpnExtensionAbility

你可能觉得,写个VPN而已,用现成的协议不就行了?WireGuard、OpenVPN,哪个不能跑?

但问题在于,我需要的是一个能跟系统深度集成的VPN。不是那种跑在应用层的代理,而是能让整个系统的网络流量都走我自定义路由的VPN。在OpenHarmony上,唯一能做到这一点的,就是VpnExtensionAbility。

它是OpenHarmony为VPN应用提供的一个扩展能力。通过它,你可以创建一个虚拟的网络接口,接管系统的所有网络流量,然后在自己的代码里决定怎么处理这些流量——是转发到远程服务器,还是直接丢弃,或者记录日志。

更重要的是,它让我能完全控制IP包的流向。这意味着我可以自定义路由策略,让某些流量走代理,某些流量直连,某些流量直接丢弃。对于我这种需要绕过节点黑名单、同时又要保证交易安全的人来说,这简直是量身定做的。

从零开始:创建VpnExtensionAbility

我打开DevEco Studio,新建了一个项目。选择“Empty Ability”模板,包名用了com.myvpn.tunnel

创建VpnExtensionAbility的第一步,是在module.json5里注册扩展能力。这个文件是OpenHarmony应用的配置入口,相当于Android的AndroidManifest.xml。

json { "module": { "extensionAbilities": [ { "name": "VpnExtAbility", "type": "vpn", "srcEntrance": "./ets/VpnExtAbility/VpnExtAbility.ts", "description": "My Custom VPN Extension", "metadata": [ { "name": "ohos.extension.vpn", "resource": "$profile:vpn_config" } ] } ] } }

这里的关键是type字段必须设为vpn,这样系统才会把它识别为VPN扩展。metadata里的ohos.extension.vpn指向一个配置文件,用来声明VPN的参数——比如DNS服务器地址、允许的应用列表等等。

接下来,我创建了VpnExtAbility.ts文件。这是核心逻辑所在。

typescript import VpnExtensionAbility from '@ohos.application.VpnExtensionAbility'; import VpnManager from '@ohos.net.vpn';

export default class VpnExtAbility extends VpnExtensionAbility { onCreate() { console.log('VpnExtensionAbility created'); }

onStart(intent) { console.log('VpnExtensionAbility started'); this.startVpn(); }

onStop() { console.log('VpnExtensionAbility stopped'); this.stopVpn(); }

startVpn() { // 这里将实现VPN启动逻辑 }

stopVpn() { // 这里将实现VPN停止逻辑 } }

看起来很简单,对吧?但真正的挑战在后面。

虚拟网卡与流量拦截

VPN的核心,是创建一个虚拟的网络接口。在OpenHarmony上,这个接口由VpnManager管理。

我需要先获取一个VpnManager实例,然后调用createVpnConnection方法创建一个VPN连接。这个连接会返回一个VpnConnection对象,通过它,我可以配置虚拟网卡的参数——比如IP地址、路由表、DNS服务器。

typescript import VpnManager from '@ohos.net.vpn'; import { BusinessError } from '@ohos.base';

async startVpn() { try { const vpnManager = VpnManager.getVpnManager(); const vpnConnection = await vpnManager.createVpnConnection();

const config = {   address: '10.0.0.2',   prefixLength: 24,   gateway: '10.0.0.1',   dnsServers: ['8.8.8.8', '1.1.1.1'],   routes: [     {       address: '0.0.0.0',       prefixLength: 0     }   ],   allowedApplications: ['com.example.myapp'],   blockApplications: ['com.example.blocked'] };  await vpnConnection.setConfig(config); console.log('VPN configuration set successfully'); 

} catch (error) { let err = error as BusinessError; console.error(Failed to start VPN: ${err.message}); } }

这里我把路由设成了0.0.0.0/0,意思是所有流量都会走这个VPN接口。allowedApplicationsblockApplications可以用来控制哪些应用能走VPN,哪些不能。

但仅仅配置好虚拟网卡还不够。我需要真正处理这些流量。

流量处理:从读到写

VpnExtensionAbility提供了两个关键方法:onReadonWrite。当系统有数据包需要发送时,会调用onRead;当VPN接口收到数据包时,会调用onWrite

我在这里可以做任何事——加密、解密、转发、记录、甚至篡改。

typescript import { VpnConnection } from '@ohos.net.vpn';

class MyVpnHandler { private connection: VpnConnection | null = null;

setConnection(conn: VpnConnection) { this.connection = conn; }

onRead(buffer: ArrayBuffer): ArrayBuffer { // 这里处理从系统读取的数据包 // 比如,我可以在这里加密数据,然后发送到远程服务器 console.log(Read ${buffer.byteLength} bytes); return buffer; }

onWrite(buffer: ArrayBuffer): ArrayBuffer { // 这里处理写入系统的数据包 // 比如,解密从远程服务器收到的数据 console.log(Write ${buffer.byteLength} bytes); return buffer; } }

但这里有个问题:onReadonWrite是在系统线程中调用的,如果我在里面做耗时的操作——比如网络请求——会阻塞整个VPN。所以我需要把真正的数据处理逻辑放到工作线程里。

OpenHarmony提供了TaskPoolWorker两种并行能力。我选择了Worker,因为它更适合这种长时间的、需要持续通信的任务。

typescript import worker from '@ohos.worker';

const workerInstance = new worker.ThreadWorker('entry/ets/workers/VpnWorker.ts');

workerInstance.onmessage = (event) => { const { type, data } = event.data; if (type === 'packet') { // 处理从Worker返回的数据包 this.sendToVirtualInterface(data); } };

// 在onRead中把数据包发给Worker处理 onRead(buffer: ArrayBuffer): void { workerInstance.postMessage({ type: 'process', data: buffer }); }

系统服务查询:找到我需要的节点

现在VPN的基本框架有了,但我还需要知道该把流量转发到哪里去。这就是系统服务查询登场的时候了。

在OpenHarmony上,系统服务查询是通过@ohos.bundle.bundleManager@ohos.syscap.SystemCapability来实现的。但我要查的不是普通的系统服务,而是跟区块链节点相关的网络服务。

我写了一个服务查询模块,用来发现局域网内的节点:

typescript import { createSocket, SocketType } from '@ohos.net.socket';

async discoverNodes() { const udp = createSocket(SocketType.UDP);

// 绑定到随机端口 await udp.bind({ address: '0.0.0.0', port: 0, family: 1 });

// 发送广播包,寻找节点 const broadcastMessage = new Uint8Array([0x00, 0x01, 0x02, 0x03]); await udp.send({ data: broadcastMessage.buffer, address: { address: '255.255.255.255', port: 30303, family: 1 } });

// 监听响应 udp.on('message', (event) => { const { remoteInfo, data } = event; console.log(Found node at ${remoteInfo.address}:${remoteInfo.port}); // 把找到的节点加入路由表 this.addNode(remoteInfo.address, remoteInfo.port); }); }

这里我用UDP广播来发现局域网内的节点。但更常见的做法是,通过DNS或者DHT网络来发现节点。

对于我的场景,我需要连接的是以太坊节点。以太坊节点通常暴露JSON-RPC接口在8545端口。我写了一个服务查询,专门扫描这个端口:

typescript async scanPort(ip: string, port: number): Promise<boolean> { const tcp = createSocket(SocketType.TCP); try { await tcp.connect({ address: ip, port: port, family: 1 }); // 发送一个简单的JSON-RPC请求 const request = JSON.stringify({ jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 }); await tcp.send({ data: request }); const response = await tcp.receive(); const result = JSON.parse(response.data as string); if (result.result) { console.log(`Found Ethereum node at ${ip}:${port}`); return true; } } catch (error) { // 连接失败或响应异常 return false; } finally { tcp.close(); } return false; }

有了这个,我就能在VPN启动时,自动扫描局域网内的节点,找到可用的以太坊节点,然后把交易流量转发过去。

实战:绕过节点黑名单

回到我最初的问题——我被节点封了IP。

我的解决方案是:在VPN里内置一个智能路由模块。当检测到某个IP被节点拒绝时,自动切换出口IP。

具体做法是,在onRead里拦截所有发往节点IP的数据包,然后通过一个代理池转发出去。代理池里维护了多个IP,每个IP对应一个不同的节点。

typescript class SmartRouter { private proxyPool: Map<string, ProxyEndpoint> = new Map(); private blacklist: Set = new Set();

addProxy(ip: string, port: number) { this.proxyPool.set(ip, new ProxyEndpoint(ip, port)); }

markAsBlacklisted(ip: string) { this.blacklist.add(ip); this.proxyPool.delete(ip); // 从备用池里选一个新的IP this.switchToBackup(ip); }

route(packet: IPPacket): ProxyEndpoint | null { const destIP = packet.destinationIP; if (this.blacklist.has(destIP)) { // 目标IP被黑,走代理 return this.selectOptimalProxy(); } // 否则直连 return null; }

private selectOptimalProxy(): ProxyEndpoint | null { // 选择延迟最低的代理 let best: ProxyEndpoint | null = null; let bestLatency = Infinity; for (const [_, proxy] of this.proxyPool) { if (proxy.latency < bestLatency) { bestLatency = proxy.latency; best = proxy; } } return best; } }

这个模块会在VPN启动时,先扫描局域网内的节点,然后对每个节点进行延迟测试。如果某个节点返回了“IP banned”之类的错误,就把这个IP加入黑名单,同时从代理池里选一个可用的代理来转发流量。

安全与隐私:我的交易数据不能泄露

作为加密交易者,我最关心的就是数据安全。VPN不仅要能绕过封锁,还要保证我的交易数据不被中间人窃取或篡改。

在VpnExtensionAbility里,我可以在onWriteonRead里对数据包进行加密。比如,我可以使用AES-256-GCM对每个数据包进行加密,然后再发送到远程节点。

typescript import crypto from '@ohos.security.crypto';

class PacketEncryptor { private key: crypto.SymmetricKey | null = null;

async init() { const generator = crypto.createSymKeyGenerator('AES256'); this.key = await generator.generateSymKey(); }

encrypt(plaintext: ArrayBuffer): ArrayBuffer { if (!this.key) { throw new Error('Encryptor not initialized'); } const cipher = crypto.createCipher('AES256|GCM', this.key); // 设置IV等参数 const iv = crypto.randomBytes(12); cipher.setParams({ iv: iv }); const ciphertext = cipher.update(plaintext); const final = cipher.doFinal(); // 返回 IV + 密文 + 认证标签 return concatBuffers(iv, ciphertext, final); }

decrypt(ciphertext: ArrayBuffer): ArrayBuffer { if (!this.key) { throw new Error('Encryptor not initialized'); } // 解析 IV、密文和认证标签 const iv = ciphertext.slice(0, 12); const tag = ciphertext.slice(ciphertext.byteLength - 16); const data = ciphertext.slice(12, ciphertext.byteLength - 16);

const decipher = crypto.createDecipher('AES256|GCM', this.key); decipher.setParams({ iv: iv, tag: tag }); const plaintext = decipher.update(data); const final = decipher.doFinal(); return concatBuffers(plaintext, final); 

} }

有了这个加密模块,即使我的流量被中间人截获,对方也只能看到一堆乱码。而真正的节点,在收到加密数据后,会用我预先部署的解密服务来还原原始交易数据。

部署与测试:终于连上了

经过两天两夜的编码和调试,我终于把VpnExtensionAbility跑起来了。

在DevEco Studio里,我点击了“Run”按钮。应用安装到设备上后,弹出了一个VPN权限请求对话框。点击“允许”,VPN启动了。

我打开终端,ping了一下以太坊节点的IP:

PING 192.168.1.100 (192.168.1.100): 56 data bytes 64 bytes from 192.168.1.100: icmp_seq=0 ttl=64 time=12.345 ms 64 bytes from 192.168.1.100: icmp_seq=1 ttl=64 time=11.234 ms

通了。

然后我打开MetaMask,切换到自定义网络,RPC地址填的是我自己的节点IP。发送一笔测试交易,几秒钟后,区块浏览器上显示交易已确认。

我长舒了一口气。

那个卡在跨链桥上的交易,我后来通过手动广播交易解决了——我把交易数据通过我的VPN转发到一个没有被封的节点,然后广播出去。几分钟后,交易被矿工打包,资金终于到账了。

后续:从VPN到区块链基础设施

这次经历让我意识到,一个定制的VPN对于区块链交易者来说有多重要。不仅仅是绕过IP封锁,更是为了保护交易隐私、降低延迟、避免被MEV机器人抢跑。

我在这个VpnExtensionAbility的基础上,又加了很多功能:

  • 交易预演:在发送交易前,先在本地节点上模拟执行,确保不会失败。
  • Gas价格优化:实时监控网络拥堵情况,自动调整Gas价格,避免交易卡住。
  • 多链路由:根据目标链自动选择最优节点,比如以太坊的交易走以太坊节点,Solana的交易走Solana节点。
  • 隐私保护:所有交易数据都经过加密,即使节点运营商也无法看到交易内容。

这个项目后来被我开源了,名字叫“Tunnel Vision”。在GitHub上,它收到了不少关注,尤其是那些跟我一样被节点封过的交易者。

有人评论说:“终于不用再担心IP被封了。这个VPN让我可以放心地在任何地方做交易。”

还有人提了PR,加了对Layer 2网络的支持。

深夜的咖啡馆里,我合上笔记本电脑。窗外,城市的灯火依然明亮。区块链的世界里,新的区块正在被挖出,交易正在被确认。而我,刚刚用自己写的VPN,完成了一笔价值五个比特币的跨链交易。

这种感觉,比喝三杯浓缩咖啡还要清醒。

版权声明:

作者: 最新鸿蒙OS VPN免费节点分享

链接: https://harmonyosvpn.com/ability-mgmt/vpnextensionability-creation-system-service-query.htm

来源: harmonyosvpn.com

文章版权归作者所有,未经允许请勿转载。

最新文章

归档

标签