应用vpnExt能力建立代理

三方VPN管理模块,主要用于支持三方VPN的启动和停止功能。

HarmonyOS
2024-05-28 20:41:47
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
bysc111

核心代码解释

vpnclient模块功能:

/* 
 * Copyright (c) 2023 Huawei Device Co., Ltd. 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */ 
 
#include "napi/native_api.h" 
#include "hilog/log.h" 
 
#include <cstring> 
#include <thread> 
#include <js_native_api.h> 
#include <js_native_api_types.h> 
#include <unistd.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <thread> 
#include <sys/time.h> 
 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
 
#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) 
 
#define NETMANAGER_VPN_LOGE(fmt, ...)                                                                                  \ 
OH_LOG_Print(LOG_APP, LOG_ERROR, 0x15b0, "NetMgrVpn", "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \ 
__LINE__, ##__VA_ARGS__) 
 
#define NETMANAGER_VPN_LOGI(fmt, ...)                                                                                  \ 
OH_LOG_Print(LOG_APP, LOG_INFO, 0x15b0, "NetMgrVpn", "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \ 
__LINE__, ##__VA_ARGS__) 
 
#define NETMANAGER_VPN_LOGD(fmt, ...)                                                                                  \ 
OH_LOG_Print(LOG_APP, LOG_DEBUG, 0x15b0, "NetMgrVpn", "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \ 
__LINE__, ##__VA_ARGS__) 
 
constexpr int BUFFER_SIZE = 2048; 
constexpr int ERRORAGAIN = 11; 
 
struct FdInfo { 
  int32_t tunFd = 0; 
  int32_t tunnelFd = 0; 
  struct sockaddr_in serverAddr; 
}; 
 
static FdInfo g_fdInfo; 
static bool g_threadRunF = false; 
static std::thread g_threadt1; 
static std::thread g_threadt2; 
 
static constexpr const int MAX_STRING_LENGTH = 1024; 
static std::string GetStringFromValueUtf8(napi_env env, napi_value value) { 
  std::string result; 
  char str[MAX_STRING_LENGTH] = {0}; 
  size_t length = 0; 
  napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); 
  if (length > 0) { 
    return result.append(str, length); 
  } 
  return result; 
} 
 
static void HandleReadTunfd(FdInfo fdInfo) { 
  uint8_t buffer[BUFFER_SIZE] = {0}; 
  while (g_threadRunF) { 
    if (fdInfo.tunFd <= 0) { 
      sleep(1); 
      continue; 
    } 
 
    int ret = read(fdInfo.tunFd, buffer, sizeof(buffer)); 
    if (ret <= 0) { 
      if (errno != ERRORAGAIN) { 
        sleep(1); 
      } 
      continue; 
    } 
 
    // Read the data from the virtual network interface and send it to the client through a TCP tunnel. 
    NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret); 
    ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr)); 
    if (ret <= 0) { 
      NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", 
        inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret, 
        strerror(errno)); 
      continue; 
    } 
  } 
} 
 
static void HandleTcpReceived(FdInfo fdInfo) { 
  int addrlen = sizeof(struct sockaddr_in); 
  uint8_t buffer[BUFFER_SIZE] = {0}; 
  while (g_threadRunF) { 
    if (fdInfo.tunnelFd <= 0) { 
      sleep(1); 
      continue; 
    } 
 
    int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr, 
    (socklen_t *)&addrlen); 
    if (length < 0) { 
      if (errno != EAGAIN) { 
        NETMANAGER_VPN_LOGE("read tun device error: %{public}d %{public}d", errno, fdInfo.tunnelFd); 
      } 
      continue; 
    } 
 
    NETMANAGER_VPN_LOGI("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", 
      inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); 
    int ret = write(fdInfo.tunFd, buffer, length); 
    if (ret <= 0) { 
      NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); 
    } 
  } 
} 
 
static napi_value TcpConnect(napi_env env, napi_callback_info info) { 
  size_t numArgs = 2; 
  size_t argc = numArgs; 
  napi_value args[2] = {nullptr}; 
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
 
  int32_t port = 0; 
  napi_get_value_int32(env, args[1], &port); 
  std::string ipAddr = GetStringFromValueUtf8(env, args[0]); 
 
  NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); 
 
  int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); 
  if (sockFd == -1) { 
    NETMANAGER_VPN_LOGE("socket() error"); 
    return 0; 
  } 
 
  struct timeval timeout = {1, 0}; 
setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); 
 
memset(&g_fdInfo.serverAddr, 0, sizeof(g_fdInfo.serverAddr)); 
g_fdInfo.serverAddr.sin_family = AF_INET; 
g_fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr 
g_fdInfo.serverAddr.sin_port = htons(port);                      // port 
 
NETMANAGER_VPN_LOGI("Connection successful\n"); 
 
napi_value tunnelFd; 
napi_create_int32(env, sockFd, &tunnelFd); 
return tunnelFd; 
} 
 
static napi_value StartVpn(napi_env env, napi_callback_info info) { 
  size_t numArgs = 2; 
  size_t argc = numArgs; 
  napi_value args[2] = {nullptr}; 
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
 
  napi_get_value_int32(env, args[0], &g_fdInfo.tunFd); 
  napi_get_value_int32(env, args[1], &g_fdInfo.tunnelFd); 
 
  if (g_threadRunF) { 
    g_threadRunF = false; 
    g_threadt1.join(); 
    g_threadt2.join(); 
  } 
 
  g_threadRunF = true; 
  std::thread tt1(HandleReadTunfd, g_fdInfo); 
  std::thread tt2(HandleTcpReceived, g_fdInfo); 
 
  g_threadt1 = std::move(tt1); 
  g_threadt2 = std::move(tt2); 
 
  NETMANAGER_VPN_LOGI("StartVpn successful\n"); 
 
  napi_value retValue; 
  napi_create_int32(env, 0, &retValue); 
  return retValue; 
} 
 
static napi_value StopVpn(napi_env env, napi_callback_info info) { 
  size_t argc = 1; 
  napi_value args[1] = {nullptr}; 
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
 
  int32_t tunnelFd; 
  napi_get_value_int32(env, args[0], &tunnelFd); 
  if (tunnelFd) { 
    close(tunnelFd); 
    tunnelFd = 0; 
  } 
 
  if (g_threadRunF) { 
    g_threadRunF = false; 
    g_threadt1.join(); 
    g_threadt2.join(); 
  } 
 
  NETMANAGER_VPN_LOGI("StopVpn successful\n"); 
 
  napi_value retValue; 
  napi_create_int32(env, 0, &retValue); 
  return retValue; 
} 
 
EXTERN_C_START 
static napi_value Init(napi_env env, napi_value exports) { 
  napi_property_descriptor desc[] = { 
    {"tcpConnect", nullptr, TcpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, 
  {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 
  {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 
}; 
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 
return exports; 
} 
EXTERN_C_END 
 
static napi_module demoModule = { 
  .nm_version = 1, 
  .nm_flags = 0, 
  .nm_filename = nullptr, 
  .nm_register_func = Init, 
  .nm_modname = "entry", 
  .nm_priv = ((void *)0), 
  .reserved = {0}, 
}; 
 
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { 
  NETMANAGER_VPN_LOGI("vpn 15b0 HELLO ~~~~~~~~~~"); 
  napi_module_register(&demoModule); 
}

vpnmoudle配置:

import Want from '@ohos.app.ability.Want'; 
import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility'; 
import vpn_client from 'libvpn_client.so'; 
import vpnExt from '@ohos.net.vpnExtension'; 
import hilog from '@ohos.hilog'; 
import common from '@ohos.app.ability.common'; 
 
const TAG: string = "[MyVpnExtAbility]"; 
let g_tunFd = -1; 
let g_tunnelFd = -1; 
 
export default class MyVpnExtAbility extends VpnExtensionAbility { 
  private VpnConnection: vpnExt.VpnConnection 
  private vpnServerIp: string = '192.168.1.115'; 
  private tunIp: string = '10.0.0.5'; 
  private blockedAppName: string = 'com.example.myvpndemo'; 
 
  onCreate(want: Want) { 
    console.info(TAG, `xdw onCreate, want: ${want.abilityName}`); 
    this.VpnConnection = vpnExt.createVpnConnection(this.context); 
    console.info("wmw createVpnConnection success") 
    this.CreateTunnel(); 
    this.Protect(); 
    this.SetupVpn(); 
    console.info("xdw step4") 
  } 
 
  onRequest(want: Want, startId: number) { 
    console.info(TAG, `xdw onRequest, want: ${want.abilityName}`); 
  } 
 
  onConnect(want: Want) { 
    console.info(TAG, `xdw onConnect, want: ${want.abilityName}`); 
    // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信 
    let abilityName: string = want.parameters.abilityName.toString(); 
    let bundleName: string = want.parameters.bundleName.toString(); 
    this.SetupVpn(); 
    return null; 
  } 
 
  onDisconnect(want: Want) { 
    console.info(TAG, `xdw onDisconnect, want: ${want.abilityName}`); 
  } 
 
  onDestroy() { 
    this.Destroy(); 
    console.info(TAG, `xdw onDestroy`); 
  } 
 
 
  CreateTunnel() { 
    console.info("xdw step1") 
    g_tunnelFd = vpn_client.tcpConnect(this.vpnServerIp, 8888); 
  } 
 
  Protect() { 
    console.info("xdw step2") 
    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect'); 
    this.VpnConnection.protect(g_tunnelFd).then(() => { 
      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect Success'); 
    }).catch((err : Error) => { 
      hilog.error(0x0000, 'developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? ''); 
    }) 
  } 
 
  SetupVpn() { 
    console.info("xdw step3") 
    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn SetupVpn'); 
    class Address { 
      address: string; 
      family: number; 
 
      constructor(address: string, family: number) { 
        this.address = address; 
        this.family = family; 
      } 
    } 
 
    class AddressWithPrefix { 
      address: Address; 
      prefixLength: number; 
 
      constructor(address: Address, prefixLength: number) { 
        this.address = address; 
        this.prefixLength = prefixLength; 
      } 
    } 
 
    class Config { 
      addresses: AddressWithPrefix[]; 
      mtu: number; 
      dnsAddresses: string[]; 
      trustedApplications: string[]; 
      blockedApplications: string[]; 
 
      constructor( 
        tunIp: string, 
        blockedAppName: string 
      ) { 
        this.addresses = [ 
          new AddressWithPrefix(new Address(tunIp, 1), 24) 
        ]; 
        this.mtu = 1400; 
        this.dnsAddresses = ["114.114.114.114"]; 
        this.trustedApplications = []; 
        this.blockedApplications = [blockedAppName]; 
      } 
    } 
 
    let config = new Config(this.tunIp, this.blockedAppName); 
 
    try { 
      this.VpnConnection.create(config).then((data) => { 
        g_tunFd = data; 
        hilog.error(0x0000, 'developTag', 'tunfd: %{public}s', JSON.stringify(data) ?? ''); 
        vpn_client.startVpn(g_tunFd, g_tunnelFd); 
      }) 
    } catch (error) { 
      hilog.error(0x0000, 'developTag', 'vpn setUp fail: %{public}s', JSON.stringify(error) ?? ''); 
    } 
  } 
 
  Destroy() { 
    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy'); 
    vpn_client.stopVpn(g_tunnelFd); 
    this.VpnConnection.destroy().then(() => { 
      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy Success'); 
    }).catch((err : Error) => { 
      hilog.error(0x0000, 'developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? ''); 
    }) 
  } 
}

实现效果

注明适配的版本信息

IDE版本:4.1.3.500

SDK版本:HarmonyOS Next Developer Preview 1

分享
微博
QQ
微信
回复
2024-05-29 21:46:10
相关问题
基于原生能力的跨应用跳转
332浏览 • 1回复 待解决
三方应用如何获取http代理信息
687浏览 • 1回复 待解决
网络地址建立socket连接
269浏览 • 1回复 待解决
Visual Studio Code如何配置代理
84413浏览 • 1回复 待解决
后台代理提醒,定时闹钟提醒功能。
367浏览 • 1回复 待解决
关于代理层,ProxySQL 会做什么?
2489浏览 • 1回复 待解决
ArkTS后台代理提醒,需要如何处理?
374浏览 • 1回复 待解决
没有配置HTTP代理,下载Android SDK失败
10524浏览 • 1回复 待解决
webview组件能建立本地服务器吗?
875浏览 • 1回复 待解决
抓取http请求包代理配置添加
241浏览 • 1回复 待解决
设置代理不跳转怎么回事?
4483浏览 • 1回复 待解决
httpclient三方库代理请求关闭问题
647浏览 • 1回复 待解决
DevEco提供哪些开放能力
4823浏览 • 1回复 待解决
使用componentSnapshot.get能力报错
334浏览 • 1回复 待解决
如何使用Sqlite全文检索能力
435浏览 • 1回复 待解决