0 前言

Hi,好久没有写博客,因为近段时间没有新的开源项目给大家。现在终于又写了一篇,是关于网络方向的内容,希望对部分读者有帮助。

1 WinDivert介绍

WinDivert是windows下为数不多的非常优秀网络库,非常适合用于开发抓包或修改包的应用程序,其拥有以下能力:

  • 抓取网络数据包
  • 过滤或丢弃网络数据包
  • 嗅探网络数据包
  • 注入网络数据包
  • 修改网络数据包

同时WinDivert还提供了完整的loopback(回环)IP、IPv6的支持,简约而强大的Api、高级别的过滤语言(可以想象为sql一样的东西)。

如此优秀的项目自然有着各个语言的二次封装项目,我在github上也找到了对应多个的dotnet封装项目,但无一例外,他们封装的比较简陋或太过于简陋,下面是封装项目的一些不足之处:

  1. IPHeader、TcpHeader、UdpHeader等未提供网络和主机的Endian转换
  2. 局限于PInvoke,没有意识使用dotnet的对象(比如IPv4直接声明为uint类型)
  3. 没有面向对象的封装,甚至简陋到只有声明了static的PInvoke方法
  4. 过滤语言没有任何处理,使用时要翻阅WinDivert的文档(写手sql一个感觉)
  5. 没有异步IO封装,都是清一色的IO同步阻塞(异步IO封装难度大)

2 WindivertDotnet介绍

WindivertDotnet是面向对象的WinDivert的dotnet异步封装,其保持着完整的底层库能力,又提供dotnet的完美语法来操作:

  • Filter对象支持Lambda构建filter language,脱离字符串的苦海;
  • 内存安全的WinDivert对象,基于IOCP的ValueTask异步发送与接收方法;
  • 内存安全的WinDivertPacket对象,提供获取包有效数据长度、解包、重构chucksums等;
  • WinDivertParseResult提供对解包的数据进行精细修改,修改后对WinDivertPacket直接生效;

2.1 网络和主机的Endian自动转换

由于windows平台是LittleEndian,而标准的IPHeader、TcpHeader、UdpHeader网络定义都是BigEndian,如果未做任何处理,当接收到一个SrcPort为80、DstPort为443的Tcp包时映射为结构体时,你调式会看到如下结果:

字段 调试看到的值 要理解为的值
SrcPort 20480 80
DstPort 47873 443

由于没有做Endian自动转换,在调试时看到的数据甚至让人抓狂,此时如果你把SrcPort改为我们理解为81端口,你是不能直接写xxx.SrcPort = 81这样的csharp代码的,应该是xxx.SrcPort = 20736

WindivertDotnet项目花了很大的时间精力,为所有涉及的结构体字段访问时都做了必要的Endian读取和写入自动转换,让调用者不再为Endian问题费脑子。

2.2 结合使用dotnet类型

IPv4地址占用4字节,IPv6地址占用16字节,所以一些封装项目直接在结构体声明为uint SrcAddrfixed uint SrcAddr[4],当然这些声明是没有错误,但是你叫使用者怎么使用呢,使用者往往是var ipAddress = IPAddress.Parse("1.2.3.4)"得到一个IPAddress类型,他们没有精力去研究怎么把IPAddress转为你的uint或uint[4],或者从uint或uint[4]转换为IPAddress类型,再加上使用了uint,又得注意Endian的转换,造成这种封装离实际应用太遥远。

WindivertDotnet在声明字段类型时,当存在对应的dotnet高级类型时,优先使用这些高级类型,除了IPAddress之外,如果字段可以使用枚举的,也都声明为了枚举类型,甚至在修改这些属性值时,有严格的输入校验。

2.3 面向对象的封装

WindivertDotnet将零散的过程式c-api,包装为多种对象,而不是让你面对满天飞的各种静态方法PInvoke调用IntPrt句柄和维护这些句柄的生命周期,例如WinDivertPacket对象,其本质是一个非托管的缓冲区内存,在没有封装之前,它就是一个csharp的IntPrt类型,看到这个类型,你得加个八倍镜观察可以做为参数传给哪些静态Api方法,同时确保不要忘记不使用之后,要手动去释放它,否则内存就一直占用。

Api 原Api
int Capacity { get; }
int Length { get; set;}
Span Span { get; }
void Clear()
Span GetSpan(int, int)
bool CalcChecksums(WinDivertAddress, ChecksumsFlag) WinDivertHelperCalcChecksums
bool CalcNetworkIfIdx(WinDivertAddress )
bool CalcOutboundFlag(WinDivertAddress)
bool CalcLoopbackFlag(WinDivertAddress)
bool DecrementTTL() WinDivertHelperDecrementTTL
int GetHashCode() WinDivertHelperHashPacket
int GetHashCode(long) WinDivertHelperHashPacket
WinDivertParseResult GetParseResult() WinDivertHelperParsePacket
void Dispose()

2.4 Filter

filter language是WinDivert引以为豪的设计,对WinDivert来说就像是从0到1发明了sql一样,它允许使用简单的文本表达式来让驱动层高性能地过滤得自己感兴趣的数据包,比如outbound and !loopback and (tcp.DstPort == 80 or udp.DstPort == 53),这种filter的作用,想必使用过wireshark软件的都特别明白。

不足的是,人们在做dotnet封装时,仅仅做了Invoke(string filter)这种传话筒式的封装,好家伙,filter language一共100个字段左右,我保证使用者不翻看filter language宝典的话,肯定不知道怎么构造这个string内容,您好歹从语法层面超越一下,提供一下filter的Builder也好啊。

WindivertDotnet提供Filter类型使用Lambda来构造这个filter language,有了它您不再需要珍藏filter language葵花宝典了,就像使用了EF之后不会sql又何妨呢,因为如下的csharp代码,每个人都打得出:

var filter = Filter.True
.And(f => f.Network.Outbound && !f.Network.Loopback)
.And(f => f.Tcp.DstPort == 80 || f.Udp.DstPort == 53);

2.5 异步IO封装

没有async和await的IO,那不是完美的IO,WinDivert提供了可选的LPOVERLAPPED,让上层可以使用IOCP模型,遗憾的是目前没有任何封装项目应用了这个参数,并结合IOCP模型包装为dotnet的Task或ValueTask异步模型。他们都是直接PInvoke使用了SendRecv这两个api,或者是SendEx之后又同步阻塞等待LPOVERLAPPED的完成,这种和dotnet里的 Task.Wait() 其实是一个道理,调用工作线程在IO完成之前只能干等,而没法抽身回到线程池中。

WindivertDotnetLPOVERLAPPED与IOCP模型结束,并封装为dotnet的TAP异步模型,凝结出下面两个核心方法:

ValueTask<int> RecvAsync(WinDivertPacket, WinDivertAddress, CancellationToken);

ValueTask<int> SendAsync(WinDivertPacket, WinDivertAddress, CancellationToken);

Api方法是简单,但过程曲折,没有资料,碰壁无数,哪怕是小小的CancellationToken参数,但它却能让pendding的IO操作撤销下来。

3 后话

因FastGithub项目的需要,所以本项目才得以诞生,现在我是结合实际项目的中使用痛点来改进本项目,甚至添加了一些WinDivert目前没有的功能,相信本项目越来越好用。

开源WindivertDotnet的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  5. 开源:Taurus.MVC 框架

    为什么要创造Taurus.MVC: 记得被上一家公司忽悠去负责公司电商平台的时候,情况是这样的: 项目原版是外包给第三方的,使用:WebForm+NHibernate,代码不堪入目,Bug无限,经常点 ...

  6. 终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了

    前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说 ...

  7. 开源:ASP.NET Aries 开发框架

    前言: 随着岁月的推进,不知不觉已在.NET这领域上战斗了十年了. 青春还没来得急好好感受,却已是步入健忘之秋的老人一枚了. 趁着还有点记忆,得赶紧把硬盘里那私藏的80G除外的东西,和大伙分享分享. ...

  8. Taurus.MVC 2.2 开源发布:WebAPI 功能增强(请求跨域及Json转换)

    背景: 1:有用户反馈了关于跨域请求的问题. 2:有用户反馈了参数获取的问题. 3:JsonHelper的增强. 在综合上面的条件下,有了2.2版本的更新,也因此写了此文. 开源地址: https:/ ...

  9. Taurus.MVC 2.0 开源发布:WebAPI开发教程

    背景: 有用户反映,Tausus.MVC 能写WebAPI么? 能! 教程呢? 嗯,木有! 好吧,刚好2.0出来,就带上WEBAPI教程了! 开源地址: https://github.com/cyq1 ...

随机推荐

  1. 一文详解 implementation api embed

    最近使用 Android Studio 从事项目开发时,发现对 implementation.api.embed 的用法了解的不是很清楚,这里准备一篇文章对其使用场景或者说是使用方式进行一个总结. d ...

  2. Apache DolphinScheduler 荣获国外知名媒体采访

    Apache DolphinScheduler 毕业的消息被北美科技媒体 TheNewStack 关注并邀请Apache DolphinScheduler PMC chair 代立冬 进行相关采访. ...

  3. ZOJ 3537 (凸包 + 区间DP)(UNFINISHED)

    #include "Head.cpp" const int N = 10007; int n, m; struct Point{ int x,y; bool operator &l ...

  4. Excel 逻辑函数(二):AND 和 OR

    前言 AND 函数是且:OR 函数是或.AND 函数中的条件必须都满足才返回 TRUE:OR 函数中的条件只要满足一个就返回 TRUE.AND 和 OR 的参数最多允许有 30 个. AND [题目] ...

  5. MQ系列4:NameServer 原理解析

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 1 关于NameServer 上一节的 MQ系列3:RocketMQ 架构分析,我们大致介绍了 ...

  6. .NET 开源工作流: Slickflow流程引擎高级开发(十) -- BpmnJS流程设计器集成

    前言: 在Slickflow产品开发过程中,前端流程设计器经历了几个不同的版本(jsPlumb, mxGraph等),目的是为了在设计流程时的用户体验更加良好,得到客户的好评和认可.BpmnJS流程设 ...

  7. 一 策略模式 来自CBF4LIFE 的设计模式

    刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开解决棘手问题,嘿,还别说,真是解决了大问题,搞到后是周瑜陪了夫人又折兵呀,那咱们先看看这个场景是什么样子的. 先说这个场景中 ...

  8. KingbaseES 命令行安装数据库

    关键字: ​ KingbaseES.Linux.x86-64 一.安装前环境准备 1.硬件环境支持 ` 金仓数据库管理系统KingbaseES支持X86.X86_64,同时支持龙芯.飞腾等国产CPU硬 ...

  9. 【读书笔记】C#高级编程 第二十五章 事务处理

    (一)简介 事务的主要特征是,任务要么全部完成,要么都不完成. (二)概述 事务由事务管理器来管理和协调.每个影响事务结果的资源都由一个资源管理器来管理.事务管理器与资源管理器通信,以定义事务的结果. ...

  10. 【读书笔记】C#高级编程 第二十四章 文件和注册表操作

    (一)文件和注册表 对于文件系统操作,相关的类几乎都在System.IO名称空间中,而注册表操作由System.Win32名称空间中的类来处理. (二)管理文件系统 System.MarshalByR ...