由于工作需要,经常需要远程客户的服务器,但是并不是所有服务器都能开外网端口,使用向日葵等软件终究还是不太方便,于是找了很多工具,包括zerotier 等,但是由于服务器在国外等有时候还不同,

于是开始自己想办法研究一个属于自己的组网工具,最后找到snltty大佬的 https://github.com/snltty/p2p-tunnel ,学习后发现是基于tun2socks实现的,

tun2socks 的优点是 把虚拟网卡的数据都打包到socket代理了,但是过滤了ping (ICmp)的包,他自行返回了 成功,这不是我要的效果

于是看了一下tun2socks 的实现,是基于tun/tap实现的,于是研究了一下,手动基于tun/tap实现了一个简易的

核心代码

  1  [SupportedOSPlatform("windows")]
2 public class WinTunDriveHostedService : TunDriveHostedService
3 {
4 private readonly static string DriverPath = AppDomain.CurrentDomain.BaseDirectory + "Drivers";
5 private const string AdapterKey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
6 private const string ConnectionKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
7
8
9 public const int TAP_WIN_IOCTL_GET_MAC = 1;
10 public const int TAP_WIN_IOCTL_GET_VERSION = 2;
11 public const int TAP_WIN_IOCTL_GET_MTU = 3;
12 public const int TAP_WIN_IOCTL_GET_INFO = 4;
13 public const int TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT = 5;
14 public const int TAP_WIN_IOCTL_SET_MEDIA_STATUS = 6;
15 public const int TAP_WIN_IOCTL_CONFIG_DHCP_MASQ = 7;
16 public const int TAP_WIN_IOCTL_GET_LOG_LINE = 8;
17 public const int TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT = 9;
18 public const int TAP_WIN_IOCTL_CONFIG_TUN = 10;
19
20 public const uint FILE_ATTRIBUTE_SYSTEM = 0x4;
21 public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
22 public const uint METHOD_BUFFERED = 0;
23 public const uint FILE_ANY_ACCESS = 0;
24 public const uint FILE_DEVICE_UNKNOWN = 0x22;
25 public WinTunDriveHostedService(IOptions<TunDriveConfig> tunDriveConfigOptions, ILogger<WinTunDriveHostedService> logger) : base(tunDriveConfigOptions, logger)
26 {
27 }
28 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
29 public static extern bool DeviceIoControl(SafeHandle device, uint IoControlCode, IntPtr InBuffer, uint InBufferSize, IntPtr OutBuffer, uint OutBufferSize, ref uint BytesReturned, IntPtr Overlapped);
30
31
32 protected override FileStream OpenDrive()
33 {
34 var className = InstallOrGetClassNameDrive();
35 var safeFileHandle = System.IO.File.OpenHandle($@"\\.\\Global\\{className}.tap", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);
36 return new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500);
37 }
38 protected virtual string InstallOrGetClassNameDrive()
39 {
40 using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(ConnectionKey))
41 {
42 var names = registryKey.GetSubKeyNames();
43 foreach (var name in names)
44 {
45 using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
46 {
47 if (connectionRegistryKey != null && connectionRegistryKey.GetValue("Name").ToString() == TunDriveConfig.TunDriveName)
48 {
49 return name;
50 }
51 }
52 }
53
54 Directory.CreateDirectory(DriverPath);
55 ZipArchive zipArchive = new ZipArchive(typeof(WinTunDriveHostedService).Assembly.GetManifestResourceStream($"RemoteNetwork.{(Environment.Is64BitOperatingSystem ? "amd64" : "i386")}.zip"), ZipArchiveMode.Read);
56 foreach (ZipArchiveEntry entry in zipArchive.Entries)
57 {
58 entry.ExtractToFile(Path.Combine(DriverPath, entry.FullName), overwrite: true);
59 }
60 StartProcess(Path.Combine(DriverPath, "tapinstall.exe"), $"install OemVista.inf TAP0901", "runas", DriverPath);
61 foreach (var name in registryKey.GetSubKeyNames())
62 {
63 if (!names.Contains(name))
64 {
65 using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
66 {
67 if (connectionRegistryKey != null)
68 {
69 StartProcess("netsh", @$"interface set interface name=""{connectionRegistryKey.GetValue("Name")}"" newname=""{TunDriveConfig.TunDriveName}""");
70 return name;
71 }
72 }
73 }
74 }
75 return string.Empty;
76 }
77 }
78 private static int ParseIP(string address)
79 {
80 byte[] addressBytes = address.Split('.').Select(s => byte.Parse(s)).ToArray();
81 return addressBytes[0] | (addressBytes[1] << 8) | (addressBytes[2] << 16) | (addressBytes[3] << 24);
82 }
83 protected override void ConfigIP(string ip, string netmask)
84 {
85 StartProcess("netsh", $"interface ip set address name=\"{TunDriveConfig.TunDriveName}\" source=static addr={ip} mask={netmask} gateway=none");
86 IntPtr intPtr = Marshal.AllocHGlobal(12);
87 Marshal.WriteInt32(intPtr, 0, ParseIP(ip));
88 Marshal.WriteInt32(intPtr, 4, 0);
89 Marshal.WriteInt32(intPtr, 8,0);
90 uint lpBytesReturned = 0;
91 bool result = DeviceIoControl(TunStream.SafeFileHandle, 2228264, intPtr, 12u, intPtr, 12u, ref lpBytesReturned, IntPtr.Zero);
92 Marshal.FreeHGlobal(intPtr);
93 }
94 private static uint CTL_CODE(uint iDeviceType, uint iFunction, uint iMethod, uint iAccess)
95 {
96 return ((iDeviceType << 16) | (iAccess << 14) | (iFunction << 2) | iMethod);
97 }
98 public override bool ConnectionState(bool connection)
99 {
100 uint Length = 0;
101 IntPtr cconfig = Marshal.AllocHGlobal(4);
102 Marshal.WriteInt32(cconfig, connection ? 1 : 0);
103
104 var b = DeviceIoControl(TunStream.SafeFileHandle, CTL_CODE(FILE_DEVICE_UNKNOWN, TAP_WIN_IOCTL_SET_MEDIA_STATUS, METHOD_BUFFERED, FILE_ANY_ACCESS), cconfig, 4, cconfig, 4, ref Length, IntPtr.Zero);
105 StartProcess("netsh", $"netsh interface ipv4 set subinterface \"{TunDriveConfig.TunDriveName}\" mtu=\"1400\" store=persistent");
106 return b;
107 }
108 }

liunx 核心代码

 1 public class TunNetWorkFrameHostedService : BackgroundService
2 {
3 private readonly string exchangeHostName = "";
4 private readonly int P2PPort = 61000;
5 protected readonly ILogger<TunNetWorkFrameHostedService> _logger;
6 public static TunNetWorkFrameHostedService Instance { get; private set; }
7 private readonly UdpClient udpClient;
8 private readonly System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
9 public TunNetWorkFrameHostedService(ILogger<TunNetWorkFrameHostedService> logger, IOptions<TunDriveConfig> tunDriveConfigOptions)
10 {
11 exchangeHostName = tunDriveConfigOptions.Value.DataExchangeHostName;
12 _logger = logger;
13 Instance = this;
14 udpClient = new UdpClient(0); if (Environment.OSVersion.Platform == PlatformID.Win32NT)
15 {
16 const int SIP_UDP_CONNRESET = -1744830452;
17 udpClient.Client.IOControl(SIP_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null);
18 }
19 }
20
21
22 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
23 {
24 udpClient.BeginReceive(ReceiveCallback, udpClient);
25 while (!stoppingToken.IsCancellationRequested)
26 {
27 await udpClient.SendAsync(TunDriveHostedService.Instance.Id, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
28 await Task.Delay(1000*30, stoppingToken).ConfigureAwait(false);
29 }
30 }
31 void ReceiveCallback(IAsyncResult ar)
32 {
33 System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
34 byte[] bytes = null;
35 try
36 {
37
38 bytes = udpClient.EndReceive(ar, ref remoteEndPoint);
39
40 }
41 finally
42 {
43 udpClient.BeginReceive(ReceiveCallback, udpClient);
44 }
45 if (bytes.Length == 4)
46 {
47 return;
48 }
49 if (bytes.Length == 5)
50 {
51 if (bytes[0] == 2)
52 {
53 P2PUDPSocketHostedService.Instance.TestP2P(bytes.Skip(1).ToArray(),false);
54 }
55 return;
56 }
57
58 TunDriveHostedService.Instance.WriteFrameBuffer(bytes);
59 }
60 public virtual async Task WriteFrameBufferAsync(Memory<byte> buffer, CancellationToken stoppingToken)
61 {
62 var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);
63
64 var tunNetWorkFrameSend= P2PUDPSocketHostedService.Instance.GetP2PClient(buffer.Slice(16, 4).ToArray());
65 if (tunNetWorkFrameSend != null)
66 {
67 await tunNetWorkFrameSend.SendAsync(buffer, stoppingToken).ConfigureAwait(false);
68 return;
69 }
70 var bytes = new byte[buffer.Length + 8];
71 buffer.Slice(12, 8).CopyTo(bytes);
72 Array.Copy(buffer.ToArray(), 0,bytes,8,buffer.Length);
73 await udpClient.SendAsync(bytes, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
74 //var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);// string.Join(".", buffer.Slice(16, 4).ToArray());// span[16] << 24 | span[17] << 16 | span[18] << 8 | span[19];
75 //var sourceId = BitConverter.ToInt32(buffer.Slice(12, 4).ToArray(), 0);
76 //_logger.LogInformation($"{sourceId} 发送到{destId}");
77 }
78 /// <summary>
79 /// 发送打洞请求
80 /// </summary>
81 /// <param name="destId"></param>
82 /// <param name="stoppingToken"></param>
83 /// <returns></returns>
84 public virtual async Task SendP2PRequestAsync(byte[] destId, CancellationToken stoppingToken)
85 {
86 using (MemoryStream memoryStream = new MemoryStream()) {
87 memoryStream.Write(TunDriveHostedService.Instance.Id);
88 memoryStream.Write(destId);
89 memoryStream.WriteByte(2);
90 memoryStream.Write(TunDriveHostedService.Instance.Id);
91 await udpClient.SendAsync(memoryStream.ToArray(), exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
92 }
93
94 }
95 }

以下是远程桌面的效果

客户端运行

打洞成功

测速

代码地址

https://github.com/hn-lyf/RemoteNetwork

测试客户端

https://files.cnblogs.com/files/dotnet-org-cn/linux-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x86.zip?t=1717937932&download=true

.NET借助虚拟网卡实现一个简单异地组网工具的更多相关文章

  1. 用Qt写软件系列三:一个简单的系统工具(上)

    导言 继上篇<用Qt写软件系列二:QIECookieViewer>之后,有一段时间没有更新博客了.这次要写的是一个简单的系统工具,需求来自一个内部项目.功能其实很简单,就是查看当前当前系统 ...

  2. 基于node实现一个简单的脚手架工具(node控制台交互项目)

    实现控制台输入输出 实现文件读写操作 全原生实现一个简单的脚手架工具 实现vue-cli2源码 一.实现控制台输入输出 关于控制台的输入输出依然是基于node进程管理对象process,在proces ...

  3. 基于SOUI开发一个简单的小工具

    基于DriectUI有很多库,比如 Duilib (免费) soui (免费) DuiVision (免费) 炫彩 (界面库免费,UI设计器付费,不提供源码) skinui (免费使用,但不开放源码, ...

  4. 用Qt写软件系列三:一个简单的系统工具之界面美化

    前言 在上一篇中,我们基本上完成了主要功能的实现,剩下的一些导出.进程子模块信息等功能,留到后面再来慢慢实现.这一篇来讲述如何对主界面进行个性化的定制.Qt库提供的只是最基本的组件功能,使用这些组件开 ...

  5. java使用注解和反射打造一个简单的jdbc工具类

    a simple jdbc tools 如有转载和引用,请注明出处,谢谢 1. 定义我们需要的注解 要想实现对数据库的操作,我们必须知道数据表名以及表中的字段名称以及类型,正如hibernate 使用 ...

  6. 用Python做一个简单的翻译工具

    编程本身是跟年龄无关的一件事,不论你现在是十四五岁,还是四五十岁,如果你热爱它,并且愿意持续投入其中,必定会有所收获. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过 ...

  7. Java开发的一个简单截屏工具

    //源代码 import java.awt.*;import java.awt.datatransfer.DataFlavor;import java.awt.datatransfer.Transfe ...

  8. 一个简单的Hibernate工具类HibernateUtil

    HibernateUtil package com.wj.app.util; import org.hibernate.Session; import org.hibernate.SessionFac ...

  9. 一个简单的Servlet工具

    以前老师在项目中用过一个Sevlet的工具,就是在请求Servlet的时候带一个参数,该参数决定要执行Servlet中的方法, public class ServletUtils extends Ht ...

  10. [C#]使用TcpListener及TcpClient开发一个简单的Chat工具

    本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 本文使用的开发环境是VS2017及dotNet4.0,写此随笔的目的是给自己及新开发人员作为参 ...

随机推荐

  1. 剑指offer29(Java)-顺时针打印矩阵(简单)

    题目: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字. 示例 1: 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5 ...

  2. 力扣553(java)-最优除法(中等)

    题目: 给定一组正整数,相邻的整数之间将会进行浮点除法操作.例如, [2,3,4] -> 2 / 3 / 4 . 但是,你可以在任意位置添加任意数目的括号,来改变算数的优先级.你需要找出怎么添加 ...

  3. 智能logo免费体验|网站Logo这样设计搜索排名跟着提升

    ​简介:一个好的网站logo,不仅让用户一眼知道网站品牌传递的信息,还能提高网站专业度和丰富度,增加SEO搜索排名.今天分享下如何设计一款实用的网站logo.阿里云智能logo设计,在线免费体验log ...

  4. AHPA:开启 Kubernetes 弹性预测之门

    ​简介:阿里巴巴云原生团队和阿里达摩院决策智能时序团队合作开发 AHPA 弹性预测产品,该产品主要出发点是基于检测到的周期做"定时规划",通过规划实现提前扩容的目的,在保证业务稳定 ...

  5. Kubernetes 稳定性保障手册 -- 可观测性专题

    简介: 伴随大家对稳定性重视程度的不断提升.社区可观测性项目的火热,可观测性成为了一个很热门的话题,站在不同的角度会产生不同的理解. 我们从软件开发的生命周期出发,尝试形成对可观测性的一个宏观理解,并 ...

  6. [Ethereum] 浅谈加密商品市场 OpenSea 与 opensea-js

    OpenSea 是用于交易以太坊加密商品的网上商店,主要的商品是 ERC721.ERC1155 标准的 Token. 它的特色就在于,只需要一个部署好的智能合约,你就能在 OpenSea 提供的界面上 ...

  7. dotnet 谨慎在静态构造函数里使用锁

    在 dotnet 的最佳实践里面,不推荐在静态构造函数里面包含复杂的逻辑,其中也就包含了本文聊的和多线程相关的锁的使用.最佳做法是尽量不要在静态构造函数里面碰到任何和锁以及多线程安全相关的逻辑.本文来 ...

  8. S/4 HANA 中的 Email Template

    电子邮件是非常常见的业务需求. SAP 了解这一点,并在 S/4 HANA(cloud和on premise)中引入了非常有趣的功能--Email Template.它将CDS视图和HTML模板结合了 ...

  9. Git基本操作命令大全

    一.全局配置命令 ## 配置级别: –local(默认,高级优先):只影响本地仓库 –global(中优先级):只影响所有当前用户的git仓库 –system(低优先级):影响到全系统的git仓库 # ...

  10. Ubuntu 启用交换分区

    前言 交换分区也称之为 swap 分区,允许系统在内存不足的情况下将内存程序写入文件,防止系统卡死失去响应的情况发生. 检查现有交换分区 首先,确认系统中是否已存在交换分区或文件.在终端中输入以下命令 ...