当前位置: 首页 > news >正文

网站建设策划书风险控制域名转接的流程

网站建设策划书风险控制,域名转接的流程,建设交易网站多少钱,社区问答网站开发libtin 一、概述1. 可移植性2. 特性 二、基础知识1. PDU2. 地址类3. 地址范围类4. 网络接口5. 写pcap文件 三、嗅探1.嗅探基础2. 嗅探器配置3. 循环嗅探4. 使用迭代器嗅探6. 包对象7. 读取pcap文件8. 包的解析 四、发送包1. 发送网络层pdu2. 发送链路层pdu3. 发送和接收响应校验… libtin 一、概述1. 可移植性2. 特性 二、基础知识1. PDU2. 地址类3. 地址范围类4. 网络接口5. 写pcap文件 三、嗅探1.嗅探基础2. 嗅探器配置3. 循环嗅探4. 使用迭代器嗅探6. 包对象7. 读取pcap文件8. 包的解析 四、发送包1. 发送网络层pdu2. 发送链路层pdu3. 发送和接收响应校验和计算线程安全 五、TCP 流1. StreamFollower2. 使用流3. 处理流数据4. 结论 六、协议1. Ethernet II2. IP3. TCP 一、概述 libtin是一个高级、跨平台的c网络数据包嗅探和制作库。 它的主要目的是为c开发人员提供一种简单、高效、平台和端序无关的方法来创建需要发送、接收和操作网络数据包的工具。它使用BSD-2许可证并托管在github上。 这个库使用起来非常简单。作为一个简短的例子这是如何使用它来打印在 eth0 接口中捕获的每个TCP数据包的源和目的地址和端口: #include iostream #include tins/tins.husing namespace Tins; using namespace std;bool callback(const PDU pdu) {// Find the IP layerconst IP ip pdu.rfind_pduIP(); // Find the TCP layerconst TCP tcp pdu.rfind_pduTCP(); cout ip.src_addr() : tcp.sport() - ip.dst_addr() : tcp.dport() endl;return true; }int main() {Sniffer(eth0).sniff_loop(callback); }高层api不代表效率低下libtins的设计始终牢记效率。事实上它是最快的数据包嗅探和解释库之一。基准测试部分包含对其工作速度的一些实际测量。这个库值得信赖花费在测试库上的时间几乎与开发库的时间一样多。在撰写本文时共有624个单元测试用于检查libtin中的所有内容是否符合预期。 1. 可移植性 libtin可以在Windows、OSX以及小端和大端GNU/Linux和FreeBSD操作系统上工作。这意味着开发的嗅探应用程序交叉编译并直接在ARM或MIPS路由器或任何其他具有嗅探功能的设备上执行它只要它有足够的RAM。(libtin是~10MB) 2. 特性 libtin支持以下协议和特性: 网络数据包制作。包嗅探和自动包解释。读写PCAP文件。动态地跟踪和重组TCP流。解密WEP和WPA2(TKIP和CCMP)动态加密802.11数据帧并解释解密的内容。至少可以在以下架构上正常工作:x86, x64, ARM和MIPS(可能更多)。支持的协议: IEEE 802.11IEEE 802.3IEEE 802.1qEthernetARPIPIPv6ICMPICMPv6TCPUDPDHCPDHCPv6DNSRadioTapMPLSEAPOLPPPoESTPLLCLLCSNAPLinux Cooked CapturePPIPKTAPNULL/Loopback 二、基础知识 1. PDU libtins每一个数据包都是PDU的子类我们可以首先看一下什么是PDU对象。libtins库中实现的每个PDU(如IP、TCP、UDP等)都是一个继承了抽象类PDU的类。这个类包含可以检索实际协议数据单元大小及其类型的方法。它还包含一个名为 send 的方法该方法允许我们通过网络有效地发送数据包。 PDU 对象也支持堆叠。这意味着一个PDU对象(不考虑其实际类型)可以有0个或1个内部PDU。这是一种非常合乎逻辑的想象网络数据包的方式。假设创建了一个以太网II帧然后在其上添加一个IP数据报后面是一个TCP帧。实际上的网络帧也是这么封装的这个结构看起来像这样: PDU的内部PDU可以使用方法PDU::inner_pdu()来去检索查询 #include tins/tins.husing namespace Tins;int main() {EthernetII eth;IP *ip new IP();TCP *tcp new TCP();// tcp is ips inner pduip-inner_pdu(tcp);// ip is eths inner pdueth.inner_pdu(ip); }方法 PDU::inner_pdu(PDU*) 将给定的参数设置为被调用方的内部PDU。作为参数传递的对象必须是使用 operator new 分配的从那时起该PDU现在由其父单元拥有这意味着该对象的销毁将由其父单元处理。因此在上面的示例中没有实际的内存泄漏。在eth的析构函数中分配的IP和TCP对象都将被销毁它们的内存将被释放这个库其实帮我们管理了一部分内存。 如果我们想存储一个副本而不是实际的指针我们可以使用 PDU::clone 函数它返回PDU的具体类型的副本包括它所有堆叠的内部PDU这样就避免了隐式共享的问题。 有一种更简单的方法来嵌套pdu。对于使用scapy的用户我们可能习惯于使用除法运算符创建PDU堆栈。libtin也支持这一点! 上面的代码可以重写如下: #include tins/tins.husing namespace Tins;int main() {// Simple stuff, no need to use pointers!EthernetII eth EthernetII() / IP() / TCP();// Retrieve a pointer to the stored TCP PDUTCP *tcp eth.find_pduTCP();// You can also retrieve a reference. This will throw a// pdu_not_found exception if there is no such PDU in this packet.IP ip eth.rfind_pduIP(); }注意在上面的例子中创建的IP和TCP临时对象是使用PDU::clone()方法克隆的。 2. 地址类 IP地址和硬件地址都使用IPv4Address, IPv6Address和 HWAddress 类处理。所有这些类都可以由std::string或c-string 字符串去构造其中包含适当的表示形式(点表示法表示IPv4Address分号表示法表示IPv6Addresses等)。 std::string lo_string(127.0.0.1);IPv4Address lo(127.0.0.1); IPv4Address empty; // represents the address 0.0.0.0// IPv6 IPv6Address lo_6(::1);// Write it to stdout std::cout Lo: lo std::endl; std::cout Empty: empty std::endl; std::cout Lo6: lo_6 std::endl;这个地址可以隐式地转换为整型值但这是在库中使用的所以我们不必担心它。如上所述我们可以注意到默认构造的IPv4Address对应于点标记的地址0.0.0.0。 这些类还提供了一个uint32_t类型的构造函数这在为函数/构造函数的某些参数使用默认值时非常有用。在上面示例的最后几行中将IPv4和IPv6地址都写入标准输出。这些类定义了输出操作符(operator)因此更容易序列化它们。 HWAddress类模板定义如下: templatesize_t n, typename Storage uint8_t class HWAddress;其中n个非类型模板参数表示地址的长度(对于网络接口通常为6)而Storage模板参数表示这n个元素中的每个元素的类型(通常不应该改变uint8_t应该这样做)。 HWAddress对象可以由std::string、c-string、const Storage*和任意长度的HWAddress组成。它们也可以比较是否相等并提供一些辅助函数来允许对地址进行迭代: HWAddress6 hw_addr(01:de:22:01:09:af);std::cout hw_addr std::endl; std::cout std::hex; // prints individual bytes for (auto i : hw_addr) {std::cout static_castint(i) std::endl; }3. 地址范围类 libtin还支持地址范围。这对于几个目的非常有用例如将流量分类到不同的子网中。 创建地址范围是非常直观的使用斜线点或网络掩码很像计算机网络的掩码表示方法: /* IPv4 */// 192.168.1.0-255 IPv4Range range1 IPv4Address(192.168.1.0) / 24;// Same as above IPv4Range range2 IPv4Range::from_mask(192.168.1.0, 255.255.255.0);/* IPv6 */// dead:0000:0000:0000:0000:0000:0000:0000-00ff IPv6Range range3 IPv6Address(dead::) / 120;// Same as above IPv6Range range4 IPv6Range::from_mask(dead::, ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00);现在我们能用地址范围做什么?我们既可以迭代它也可以询问它是否有一个特定的地址在该网络内: IPv4Range range IPv4Address(192.168.1.0) / 24;range.contains(192.168.1.250); // Yey, it belongs to this network, return true range.contains(192.168.0.100); // NOPE, return false// print all address for (const auto addr : range) {std::cout addr std::endl; }但是等等还有更多。我们还可以创建硬件地址范围。为什么这个有用?使用它我们可以使用OUI说明符来确定哪个是特定网络设备的供应商: // Some OUI which belongs to Intel auto range HWAddress6(00:19:D1:00:00:00) / 24;// Does this address belong to Intel? if (range.contains(00:19:d1:22:33:44)) {std::cout Its Intel! std::endl; }4. 网络接口 这里回顾的最后一个helper类是NetworkInterface。该类表示网络接口的抽象。它可以从网口的名称(作为字符串)或者IPv4Address 构造。最后一个构造函数创建了一个接口如果某个数据包被发送到给定的ip地址该接口将作为网关: NetworkInterface lo(lo); // this would be lo NetworkInterface lo1(IPv4Address(127.0.0.1));我们还可以使用NetworkInterface::name()检索网络接口的名称。请注意该函数在每次调用时都会搜索系统的接口并检索名称因此最好调用它一次并存储返回值这样节约时间。 5. 写pcap文件 pcap 文件就是 Wireshark的保存网络数据包的一种格式。 参考 https://wiki.wireshark.org/Development/LibpcapFileFormat#File_Format 向pcap文件写入数据包也非常简单。packketwriter类接受要在其中存储数据包的文件的名称作为参数以及一个数据链路类型该类型指示哪一层将被写入文件。这意味着如果我们正在编写EthernetII pdu我们应该使用DataLinkTypeEthernetII标志而在无线接口上我们应该使用DataLinkTypeRadioTap或DataLinkTypeDot11具体的取值取决于设备中使用的封装。 // Well write packets to /tmp/test.pcap. Use EthernetII as the link // layer protocol. PacketWriter writer(/tmp/test.pcap, DataLinkTypeEthernetII());// Now create another writer, but this time well use RadioTap. PacketWriter other_writer(bleh.pcap, DataLinkTypeRadioTap());创建PacketWriter后可以使用PacketWriter::write方法向其写入pdu。这个方法包含2个重载:一个接受一个PDU另一个接受两个模板转发迭代器start和end。后者将遍历范围[start, end]并写入存储在范围每个位置的pdu。如果*start产生一个PDU或者解解它几次导致一个PDU这都可以工作。这意味着std::vectorstd::unique_ptr::迭代器也可以工作。 这个例子创建了一个std::vector其中包含一个EthernetII PDU并使用两个重载将其写入pcap文件: #include tins/tins.h #include vectorusing namespace Tins;int main() {// Well write packets to /tmp/test.pcap. The lowest layer will be // EthernetII, so we use the appropriate identifier.PacketWriter writer(/tmp/test.pcap, DataLinkTypeEthernetII());// A vector containing one EthernetII PDU.std::vectorEthernetII vec(1, EthernetII(00:da:fe:13:ad:fa));// Write the PDU(s) in the vector(only one, in this case).writer.write(vec.begin(), vec.end());// Write the same PDU once again, using another overload.writer.write(vec[0]); }现在使用上面列出的大多数类来创建一个数据包并发送它: #include tins/tins.h #include cassert #include iostream #include stringusing namespace Tins;int main() {// Well use the default interface(default gateway)NetworkInterface iface NetworkInterface::default_interface();/* Retrieve this structure which holds the interfaces IP, * broadcast, hardware address and the network mask.*/NetworkInterface::Info info iface.addresses();/* Create an Ethernet II PDU which will be sent to * 77:22:33:11:ad:ad using the default interfaces hardware * address as the sender.*/EthernetII eth(77:22:33:11:ad:ad, info.hw_addr);/* Create an IP PDU, with 192.168.0.1 as the destination address* and the default interfaces IP address as the sender.*/eth / IP(192.168.0.1, info.ip_addr);/* Create a TCP PDU using 13 as the destination port, and 15 * as the source port.*/eth / TCP(13, 15);/* Create a RawPDU containing the string Im a payload!.*/eth / RawPDU(Im a payload!);// The actual senderPacketSender sender;// Send the packet through the default interfacesender.send(eth, iface); }该数据包的创建可以在一行中完成使用operator/而不是operator/: // same as above, just shorter EthernetII eth EthernetII(77:22:33:11:ad:ad, info.hw_addr) / IP(192.168.0.1, info.ip_addr) /TCP(13, 15) /RawPDU(Im a payload!);三、嗅探 1.嗅探基础 嗅探是通过Sniffer类完成的。这个类接受一个libpcap字符串过滤器让我们去对某些网络设备抓包。 一旦设置了过滤器就有两个函数允许检索嗅探包。 Sniffer::next_packetSniffer::sniff_loop 第一个是Sniffer::next_packet。这个成员函数允许我们使用提供的过滤器来检索数据包: // We want to sniff on eth0. This will capture packets of at most 64 kb. Sniffer sniffer(eth0);// Only retrieve IP datagrams which are sent from 192.168.0.1 sniffer.set_filter(ip src 192.168.0.1);// Retrieve the packet. PDU *some_pdu sniffer.next_packet(); // Do something with some_pdu... .... // Delete it. delete some_pdu;2. 嗅探器配置 从3.2版开始有一个类表示可以提供给嗅探器的不同参数以影响嗅探会话。它们都是不同libpcap函数的包装器比如pcap_setfilter、pcap_set_promise等。这是对其他Sniffer构造函数所采用的许多参数的改进。 例如如果我们想捕获端口80上的数据包在混杂模式下嗅探并设置快照长度为400字节我们可以这样做: // Create sniffer configuration object. SnifferConfiguration config; config.set_filter(port 80); config.set_promisc_mode(true); config.set_snap_len(400);// Construct a Sniffer object, using the configuration above. Sniffer sniffer(eth0, config);注意如果我们注意到嗅探到的数据包是突发的或者它们的捕获有延迟(例如1秒)这很可能是由于libpcap v1.5默认使用缓冲模式。如果我们希望尽可能快地获取数据包请确保使用 SnifferConfiguration::set_immediate_mode 使用立即模式。 3. 循环嗅探 除了Sniffer::next_packet之外还有另一种方法可以从Sniffer对象中提取数据包。很常见的情况是我们希望嗅探大量数据包直到满足某些特定条件。在这种情况下最好使用Sniffer::sniff_loop。 该方法接受一个模板函函数作为实参实参必须定义一个具有以下签名之一的操作符: bool operator()(PDU); bool operator()(const PDU);// These are only allowed when compiling in C11 mode. bool operator()(Packet); bool operator()(const Packet);对 Sniffer::sniff_loop 的调用将使嗅探器开始处理数据包。将使用每个处理过的包作为其参数调用回调函数。如果在某个时刻我们想停止嗅探那么我们的回调函数应该返回false。如果继续返回true, 那么Sniffer对象将继续循环。 函数对象将是复制构造的因此它必须实现复制语义。有一个辅助模板函数它接受一个指向模板参数类型对象的指针和一个成员函数并返回一个HandlerProxy。该对象实现了所需的操作符在该操作符中它使用给定的对象指针将调用转发给所提供的成员函数指针: #include tins/tins.husing namespace Tins;bool doo(PDU) {return false; }struct foo {void bar() {SnifferConfiguration config;config.set_promisc_mode(true);config.set_filter(ip src 192.168.0.100);Sniffer sniffer(eth0, config);/* Uses the helper function to create a proxy object that* will call this-handle. If youre using boost or C11,* you could use boost::bind or std::bind, that will also* work.*/sniffer.sniff_loop(make_sniffer_handler(this, foo::handle));// Also validsniffer.sniff_loop(doo);}bool handle(PDU) {// Dont process anythingreturn false;} };int main() {foo f;f.bar(); }正如我们所看到的使用 Sniffer::sniff_loop 进行嗅探不仅是处理多个数据包的简单方法而且在使用类时还可以使代码更加整洁。 在上面的例子中我们知道我们正在嗅探IP地址 192.168.0.100 发送的IP pdu但是我们的函数接受一个PDU。我们想要搜索存储在参数内的IP PDU(可能是EthernetII类型)。幸运的是我们可以要求PDU在其整个PDU堆栈(包括其自身)中搜索某个PDU类型并返回对它的引用也就是会一直找。如果报文中没有找到PDU则抛出pdu_not_found异常: bool doo(PDU some_pdu) {// Search for it. If there is no IP PDU in the packet, // the loop goes onconst IP ip some_pdu.rfind_pduIP(); // non-const works as wellstd::cout Destination address: ip-dst_addr() std::endl;// Just one packet pleasereturn false; }void test() {SnifferConfiguration config;config.set_promisc_mode(true);config.set_filter(ip src 192.168.0.100);Sniffer sniffer(eth0, config);sniffer.sniff_loop(doo); }使循环嗅探机制优于逐个获取数据包的另一个原因是异常处理。Sniffer::sniff_loop捕获函函数体中抛出的pdu_not_found和malformmed_packet异常。这意味着我们可以使用PDU::rfind_pdu而不必担心是否找不到这样的PDU因为嗅探器会捕捉到异常嗅探会话将继续。 在Windows上有点区别需要去官网看Windows额外嗅探部分会有一些平台的配置。 4. 使用迭代器嗅探 还有另一种方法可以从Sniffer对象中检索数据包。这个类定义了begin()和end()两个方法它们返回前向迭代器。这些可以用来在嗅探数据包时检索数据包: Sniffer s ...; for (auto packet : s) {// packet is a Packetprocess(packet); }6. 包对象 如果我们需要存储PDU和时间戳对象那么我们应该使用Packet类。数据包包含PDU和时间戳可以复制和移动。 让我们来看一个例子在这个例子中我们将从网络中读取的10个数据包存储到一个向量中: #include vector #include tins/tins.husing namespace Tins;int main() {std::vectorPacket vt;Sniffer sniffer(eth0);while (vt.size() ! 10) {// next_packet returns a PtrPacket, which can be implicitly converted to Packet.vt.push_back(sniffer.next_packet());}// Done, now lets check the packetsfor (const auto packet : vt) {// Is there an IP PDU somewhere?if (packet.pdu()-find_pduIP()) {// Just print timestamps seconds and IP source addressstd::cout At: packet.timestamp().seconds() - packet.pdu()-rfind_pduIP().src_addr() std::endl;}} }我们可能已经注意到Packet对象也可以与Sniffer::next_packet一起使用: Sniffer sniffer(eth0); // PDU pointer, as mentioned at the beginning std::unique_ptrPDU pdu_ptr(sniffer.next_packet());// auto cleanup, no need to use pointers! Packet packet sniffer.next_packet(); // If there was some kind of error, packet.pdu() nullptr, // so we need to check that. if (packet) {process_packet(packet); // whatever }在Sniffer::sniff_loop上使用的函子对象也可以接受数据包但只有在c 11模式下编译时才可以。 7. 读取pcap文件 读取pcap格式的文件非常简单。FileSniffer类以要打开的文件的名称作为参数并允许我们处理其中的数据包。Sniffer和FileSniffer都继承自BaseSniffer, BaseSniffer是实际实现next_packet和sniff_loop的类。因此我们可以像在上面的例子中使用Sniffer一样使用FileSniffer类: #include tins/tins.h #include iostream #include stddef.husing namespace Tins;size_t counter(0);bool count_packets(const PDU ) {counter;// Always keep looping. When the end of the file is found, // our callback will simply not be called again.return true; }int main() {FileSniffer sniffer(/tmp/some_pcap_file.pcap);sniffer.sniff_loop(count_packets);std::cout There are counter packets in the pcap file\n; }8. 包的解析 既然我们已经了解了从网络接口读取pcap文件和嗅探的方法那么我们将了解如何执行数据包解释。 每次从其中一个源读取数据包时都会创建该源的链路层类型的对象(EthernetII, RadioTap等)。这些类型的对象中的每一个都根据其内部标志检测哪一个是下一个PDU的类型创建它将它添加为子PDU并传播相同的动作。 除传输层协议外每个实例化的PDU都执行此操作。这意味着例如如果从以太网接口嗅探到DNS数据包我们将得到以下结构: 然后我们可以使用RawPDU的有效载荷来解释构造DNS对象的DNS数据包: // This is a handler used in Sniffer::sniff_loop bool handler(const PDU pkt) {// Lookup the UDP PDUconst UDP udp pkt.rfind_pduUDP();// We need source/destination port to be 53if (udp.sport() 53 || udp.dport() 53) {// Interpret it as DNS. This might throw, but Sniffer catches itDNS dns pkt.rfind_pduRawPDU().toDNS();// Just print out each querys domain namefor (const auto query : dns.queries()) {std::cout query.dname() std::endl;}}return true; }对于其他协议(如DHCP)应该使用相同的机制。如果我们想知道为什么传输层pdu不能自动解释应用层协议原因是效率。应用层协议如DNS需要比底层协议更多的处理才能解析它们。此外有些应用程序甚至可能不需要使用这些协议因此让它们为额外的处理这样的开销是不划算的。 四、发送包 PacketSender类负责在网络上发送数据包。在内部它为不同的套接字层(例如2层和3层)存储原始套接字。 当调用PacketSender::send(PDU)时PDU参数被序列化成一个字节数组并通过相应的套接字发送。 1. 发送网络层pdu 发送网络层pdu如IP和IPv6非常直观: PacketSender sender; IP pkt IP(192.168.0.1) / TCP(22) / RawPDU(foo); sender.send(pkt);注意在IP构造函数中没有指定源地址。默认情况下使用地址0.0.0.0。但是在发送网络层PDU时如果源地址为0.0.0.0则PDU会在路由表中查找哪个应该是源地址并自动设置。这已经由网络驱动程序完成了但是一些传输层协议(如TCP)在计算校验和时需要这个地址因此也必须由库完成也就是要自己组装。 2. 发送链路层pdu 在发送链路层pdu(如etherneii)时还有一件事应该记住。在这种情况下数据包必须通过特定的网络接口发送。我们可以在发送时指定: PacketSender sender; EthernetII pkt EthernetII() / IP() / TCP() / RawPDU(foo); sender.send(pkt, eth0); // send it through eth0// if youre sending multiple packets, you might want to create // the NetworkInterface object once NetworkInterface iface(eth0); sender.send(pkt, iface);这将通过eth0接口发送数据包。 使用同一个网络接口发送多个数据包是很常见的。PacketSender包含一个默认接口当使用PacketSender::send(PDU)过载时在该接口中发送链路层PDU。 PacketSender sender(eth0); EthernetII pkt EthernetII() / IP() / TCP() / RawPDU(foo); sender.send(pkt); // send it through eth0 as wellstd::cout sender.default_interface().name() std::endl; sender.default_interface(eth1); sender.send(pkt); // now were sending through eth1.注意必须带有网口信息发送不然缺省情况下该PacketSender 无效需要在发送链路层pdu之前进行设置下面就是没指定 eth0 或者 eth1 如下图所示: PacketSender sender; EthernetII pkt EthernetII() / IP() / TCP() / RawPDU(foo); sender.send(pkt); // throws invalid_interface3. 发送和接收响应 到目前为止我们已经了解了如何发送数据包但是如果我们期望对该数据包进行响应该怎么办呢?让我们以ARP请求为例。发送后我们很可能希望收到响应。 这可以通过在发送数据包时嗅探来实现检查每个嗅探的数据包直到找到响应。然而为了匹配数据包响应有必要执行几个协议相关的比较。在ARP响应的情况下这将相当简单。但是其他协议需要检查目的地址和源地址、端口、标识号等。 幸运的是库中已经包含了发送和接收机制。这可以通过使用PacketSender::send_recv来实现它提供了两个重载: PDU *send_recv(PDU pdu); PDU *send_recv(PDU pdu, const NetworkInterface iface);NetworkInterface参数的作用与PacketSender::send相同。 让我们看看如何使用它来执行ARP请求并接收其响应: // The address to resolve IPv4Address to_resolve(192.168.0.1); // The interface well use, since we need the senders HW address NetworkInterface iface(to_resolve); // The interfaces information auto info iface.addresses(); // Make the request EthernetII eth ARP::make_arp_request(to_resolve, info.ip_addr, info.hw_addr);// The sender PacketSender sender; // Send and receive the response. std::unique_ptrPDU response(sender.send_recv(eth, iface)); // Did we receive anything? if (response) {const ARP arp response-rfind_pduARP();std::cout Hardware address: arp.sender_hw_addr() std::endl; }注意在PacketSender::send_recv中从套接字读取的数据包将与发送的数据包进行匹配直到找到有效的数据包。 顺便说一句硬件地址可以很容易地解析使用Utils::resolve_hwaddr: // The sender PacketSender sender; // Will throw std::runtime_error if resolving fails HWAddress6 addr Utils::resolve_hwaddr(192.168.0.1, sender); std::cout Hardware address: addr std::endl;回到发送和接收机制我们也可以用它来确定TCP端口是否打开: // The sender PacketSender sender; // The SYN to be sent. IP pkt IP(192.168.0.1) / TCP(22, 1337); pkt.rfind_pduTCP().set_flag(TCP::SYN, 1);// Send and receive the response. std::unique_ptrPDU response(sender.send_recv(pkt)); // Did we receive anything? if (response) {TCP tcp response-rfind_pduTCP();if (tcp.get_flag(TCP::RST)) { std::cout Port is closed! std::endl;}else {std::cout Port is open! std::endl;} }作为最后一个例子下面的代码使用PacketSender::send_recv解析域名: // The sender PacketSender sender; // The DNS request IP pkt IP(8.8.8.8) / UDP(53, 1337) / DNS(); // Add the query pkt.rfind_pduDNS().add_query({ www.google.com, DNS::A, DNS::IN }); // We want the query to be resolverd recursively pkt.rfind_pduDNS().recursion_desired(1);// Send and receive the response. std::unique_ptrPDU response(sender.send_recv(pkt)); // Did we receive anything? if (response) {// Interpret the responseDNS dns response-rfind_pduRawPDU().toDNS();// Print responsesfor (const auto record : dns.answers()) {std::cout record.dname() - record.data() std::endl;} }校验和计算 在上面的示例中使用的一些协议(如IP和TCP)包含校验和字段。此校验和必须在每次发送数据包时计算。libtins会自动完成:每次数据包被序列化时(在PacketSender::send中)都会计算校验和;所以我们没有必要担心他们。 线程安全 需要注意的一点是原始套接字打开操作不是线程安全的所以如果我们有多个编写器我们应该显式地自己打开所需的套接字(这可以通过PacketSender::open_l2_socket和PacketSender::open_l3_socket来完成)。否则套接字将在需要时打开。 五、TCP 流 从3.4版开始libtin提供了一组类这些类允许以一种非常简单但功能强大的方式重组TCP流。在引入这些类之前有一个TCPStreamFollower类可以完成这种工作但是以一种不那么可扩展的不可用的方式。 这些新类的目标是提供一种非常简单的方式来跟踪流处理数据获取属性等等使用一个简单的基于回调的接口。流将处理乱序数据重新组合并让用户处理它而不必处理数据包、有效负载、序列号等。 所有这些类都需要使用c 11因为它们使用std::function作为指定回调的方式。因此我们应该使用一些相当新的编译器来使用它。如果我们使用的是GCC, 4.6可能就足够了甚至可能是更旧的版本。 1. StreamFollower 我们应该知道的主要类是StreamFollower。这个类将处理TCP数据包查看其中使用的IP地址和端口。每当看到一个新的4元组(客户端地址、客户端端口、服务器地址、服务器端口)时它将为该TCP流创建一些上下文并执行用户提供的回调来通知它的创建。之后属于该流的所有数据包将被转发到正确的对象让它处理数据并更新其内部状态。 StreamFollower的另一个职责是检测流中的错误。假设我们有一个高丢包(例如我们的程序不能足够快地处理数据包)我们不想为永远不会重新组装的流保持缓冲数据或者为实际上关闭但FIN/RST数据包没有捕获的流存储状态和数据。由于这个原因这个类将检测这些事件(太多的缓冲数据包流超时等)并在发生这种情况时删除它们的状态。 作为一个简单的例子如何创建一个StreamFollower并设置一些回调: #include tins/tcp_ip/stream_follower.husing Tins::TCPIP::Stream; using Tins::TCPIP::StreamFollower;// New stream is seen void on_new_stream(Stream stream) {}// A stream was terminated. The second argument is the reason why it was terminated void on_stream_terminated(Stream stream, StreamFollower::TerminationReason reason) {}// Create our follower Tins::TCPIP::StreamFollower follower;// Set the callback for new streams. Note that this is a std::function, so you // could use std::bind and use a member function for this follower.new_stream_callback(on_new_stream);// Now set up the termination callback. This will be called whenever a stream is // stopped being followed for some of the reasons explained above follower.stream_termination_callback(on_stream_terminated);// Now create some sniffer Sniffer sniffer ...;// And start sniffing, forwarding all packets to our follower sniffer.sniff_loop([](PDU pdu) {follower.process_packed(pdu);return true; })注意StreamFollower::process_packet有另一个接受Packet的重载。我们应该尝试使用此过载因为它将使流在实际数据包时间超时而不是使用系统时钟。 这是重组TCP流的第一步。在接下来的部分中我们将看到如何使用它做一些有用的事情。 2. 使用流 一旦在StreamFollower上配置了新流的回调我们可能想要对新流做一些事情。流允许我们为流上发生的不同事件配置回调。 每当有新的、准备处理的数据时就会生成数据事件。这意味着具有下一个预期序列号的数据包到达并且其有效负载可用加上之前可能已经接收但由于第一个数据包的数据丢失而无法处理的所有乱序有效负载。 我们可以选择订阅客户机和服务器数据事件。这意味着当每个流上有来自客户端或服务器的新数据时我们可以在不同的回调上收到通知。让我们在一个简短的例子中使用它: // This will be called when theres new client data void on_client_data(Stream stream) {// Get the clients payload, this is a vectoruint8_tconst Stream::payload_type payload stream.client_payload();// Now do something with it! }// This will be called when theres new server data void on_server_data(Stream stream) {// Process the servers data }// New stream is seen void on_new_stream(Stream stream) {// Configure the client and server data callbacksstream.client_data_callback(on_client_data);stream.server_data_callback(on_server_data);// Done! }就是这样我们之前构造的StreamFollower将继续处理数据包并将它们转发给正确的Stream对象这些对象将在适当的时候执行这些回调。 我们也可以订阅每个流上的其他事件。其中之一是close事件它在流正确关闭时执行。我们可以通过调用Stream:: stream_close_callback来实现。 3. 处理流数据 现在我们已经了解了如何使用流的基础知识让我们看看其他一些特性。 默认情况下每当流中有新数据可用时该数据将被移动到流的有效负载中数据回调将被执行然后该数据将被擦除。这样做是为了使数据不会开始缓冲从而使内存使用量上升直到流关闭(或者内存耗尽)。如果我们想缓冲数据并使用我们自己的处理方式那么我们应该调用以下函数: // New stream is seen void on_new_stream(Stream stream) {// Disables auto-deleting the clients data after the callback is executedstream.auto_cleanup_client_data(true);// Same thing for the servers datastream.auto_cleanup_server_data(true);// Or a shortcut to doing this for both:stream.auto_cleanup_payloads(true); }如果我们只计划处理客户端的数据而不是服务器的数据那么我们应该调用ignore_client/server_data。否则即使我们没有设置回调数据仍然会被缓冲并根据需要重新排序: // New stream is seen void on_new_stream(Stream stream) {// We dont even want to buffer the clients datastream.ignore_client_data(): }4. 结论 这应该使我们对如何使用StreamFollower和Stream类有了一个相当好的介绍。我们可以查看HTTP请求示例. 六、协议 libtin提供了对几种网络协议的支持。文档包含关于库中存在的每个类、方法和函数的信息。但是我们可能不希望仅仅为了学习如何制作一个简单的TCP数据包而阅读整个文档。 在本节中我们将了解我们在家庭网络中最可能看到的一些协议是如何在库中实现的。 1. Ethernet II Ethernet II 协议由以太II类表示实际上非常简单。它只包含获取和设置目标地址和源地址以及有效负载类型的方法。它还包含一个构造函数可以让我们选择性地指定这两个地址: // Both addresses are 00:00:00:00:00:00 EthernetII eth; eth.dst_addr(01:02:03:04:05:06); eth.src_addr(00:01:02:03:04:05);// Same as above, just shorter EthernetII eth2(01:02:03:04:05:06, 00:01:02:03:04:05);2. IP IP类包含更多的方法既用于访问协议字段也用于添加和检索存储的选项。在这个例子中我们将修改其中的一些字段: // Both addresses are 0.0.0.0 IP ip; ip.dst_addr(192.168.0.100); ip.src_addr(192.168.0.50);// Same as above, just shorter IP ip2(192.168.0.100, 192.168.0.50); // Set the time-to-live attribute ip2.ttl(10); // Set the type-of-service attribute ip2.tos(3);IP是支持TLV(类型-长度-值)编码选项的众多协议之一。这意味着存储在协议中的每个选项都包含一个表示其类型的字段另一个保存其长度第三个包含实际数据。 每个包含选项的类(如IP、TCP和DHCP等)的行为都是相同的。对于任何包含选项的类型T, T::option是实际存储选项的类型。每个协议提供两个成员函数: 它给那个PDU添加了一个选项。 void T::add_option(const T::option)找到一个选项。 const T::option* T::search_option(T::option::option_type)使用这些成员函数必须知道组成每个选项类型的字段的长度及其端序。由于这非常麻烦因此这些协议都为每个有效选项提供了一个getter和一个setter。如果该选项不存在于PDU中getter将总是抛出option_not_found异常。 在IP的情况下这些是一些支持的getter /setter选项: IP ip; // Sets the Stream Identifier option. ip.stream_identifier(165); // Sets the Record Route option. ip.record_route({ // Constructing a record_route_type object2, // pointer{ 192.168.0.1, 192.168.0.2 } // routes} );// Retrieve the Record Route option. IP::record_route_type routes ip.record_route(); // Echo std::cout static_castint(routes.pointer) std::endl; std::copy(routes.routes.begin(),routes.routes.end(),std::ostream_iteratorIP::address_type(std::cout, \n) );// This will throw an option_not_found exception. auto x ip.security();3. TCP TCP也包含几种选项类型因此上面提到的关于它们的所有内容仍然有效。让我们来看看如何制作一些基本的TCP帧: // Both source and destination ports are 0. TCP tcp; tcp.dport(22); tcp.sport(22334);// Same as above TCP tcp2(22, 22334);// Set the sequence number tcp.seq(0x9283); // Set the acknowledge number tcp.ack_seq(0x9283); // Set the SYN flag tcp.set_flag(TCP::SYN, 1); // This will be available as of libtins 1.2 tcp.flags(TCP::SYN | TCP::ACK);// Get the SYN flag auto s tcp.get_flag(TCP::SYN); // This will be available as of libtins 1.2 bool is_syn_ack (tcp.flags() (TCP::SYN | TCP::ACK));// Set some options tcp.sack_permitted(); if (tcp.has_sack_permitted()) {// whatever } tcp.altchecksum(TCP::CHK_8FLETCHER);
http://www.w-s-a.com/news/871225/

相关文章:

  • 天津制作网站的公司电话wordpress架设进出销
  • tomcat做静态网站prestashop和wordpress
  • 上海响应式建站wap网站微信分享代码
  • 四川建筑人才招聘网南昌网站优化
  • 南充网站建设制作重庆有的设计网站大全
  • 深圳沙井做网站公司网站搭建谷歌seo
  • 学校资源网站的建设方案山西省住房城乡建设厅网站
  • 医疗行业网站建设深圳网络科技公司排名
  • 企业形象型网站建设wordpress chess
  • 网站的域名起什么好处罗湖网站建设公司乐云seo
  • 网站的服务器在哪里sem推广软件选哪家
  • 科技网站欣赏婚庆公司经营范围
  • 网站后台管理系统php校园网站建设意见表填写
  • 网站建设问题调查常州百度推广代理公司
  • net网站开发学习谷歌优化培训
  • 企业网站公众号广东网站建设方便
  • 2008r2网站建设张店网站建设方案
  • 企业网站首页学生做的网站成品
  • 网站开发 架构设计企业信息管理系统的组成不包括
  • 网站维护模式网页传奇游戏平台排行
  • 企业网站改自适应蛋糕方案网站建设
  • 网站开发技术职责网站升级中html
  • 天网网站建设百度权重高的网站
  • 明年做哪些网站致富网站站长 感受
  • 东莞营销网站建设优化怎么做微信网站推广
  • 网站建设一个多少钱php网站服务器怎么来
  • 引流用的电影网站怎么做2012服务器如何做网站
  • 什么网站可以做推广广州安全信息教育平台
  • 网站开发具备的相关知识wordpress简约文字主题
  • asp网站伪静态文件下载seo外包公司哪家好