经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
.NET借助虚拟网卡实现一个简单异地组网工具
来源:cnblogs  作者:中国.NET研究协会  时间:2024/6/11 9:56:32  对本文有异议

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

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

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

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

核心代码

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

 

liunx 核心代码

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

 

以下是远程桌面的效果

客户端运行

 打洞成功

 测速

 

代码地址

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

原文链接:https://www.cnblogs.com/dotnet-org-cn/p/18239999

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号