ZMTP协议

ZoreMQ Transport Protocol是一个传输层协议,用于ZMQ的连接的信息交互,本文档描述的是3.0协议,主要分析基于NULL Security Mechanism


协议语法

ZMTP由三部分组成,分别是 greeting、handshake、traffic

部分描述构成
greeting描述ZMQ版本、安全机制等signature + version + mechanism + as-server + filler
handshake描述端类型,如 PUB/SUB,REQ/REP一个或多个command
traffic命令或者消息command

ZMTP Wireshark 抓包

WireShark 默认不提供ZMTP解析插件,需要自己配置,步骤如下:

插件仓库:https://github.com/whitequark/zmtp-wireshark

下载插件:https://github.com/whitequark/zmtp-wireshark/blob/master/zmtp-dissector.lua

将插件zmtp-dissector.lua放到WireShark安装目录,比如我的是:C:\Program Files\Wireshark

修改C:\Program Files\Wireshark\init.lua,在文件末尾添加

dofile(DATA_DIR.."zmtp-dissector.lua")

对基于TCP端口通讯ZMQ进行抓包,例如端口为7380,将该端口Decode As ZMTP

8607ab1ed5f523a6bbc4491ec74b924f.png
解析接如下

1b1998d082deac0fe4c96b7aafb00f94.png

greeting

greeting 固定64个字节大小,下面将依次介绍每个部分。

signature

固定10字节大小,固定值为ff 00 00 00 00 00 00 00 01 7f;

signature可以用来校验链接是否为ZMQ链接,连续读取10个字节,判断开头是否为0xff,结尾是否为0x7f

version

固定2字节大小,格式为{major_version, minor_version}3.0 协议则为03 00,实际编码过程中只会校验major_version;

mechanism

固定20字节大小,这里只介绍NULL Security Mechanism,也就是不校验,其值为NULL,剩余以内容填充0;

as-server

固定一个字节大小,0x00 或者 0x01 ,当mechanism为NULL时候,as-server必须为0

filler

填充greeting至64个字节。

抓包示意

由Wireshark解析过后的协议。

6bd633daef76a532eb2eed806a8e6485.png

Frame

greeting之后的所有数据格式都为Frame,包含commandmessage

frame的格式如下:

Frame = Flag + Payload Length + Payload

抓包示意如下
在这里插入图片描述

  • Flag
    Flag 为1字节大小,每位代表不同的意思,参考抓包解释
    在这里插入图片描述
    低1位:表示是否有更多Frame,这里用于ZMQ中sendmore属性
    低2位:表示长度是否为8字节长度,否则为1字节长度
    低3位:表示当前frame是否为Command
    其他:保留,为0

  • Payload Length
    数据长度,可以为1字节或者8字节大小,根据Flag中的标志位决定

  • Payload
    实际的数据,大小为Payload Length

handshake

此阶段用来交换对端的READY命令以及metadata,主要包含对端的类型。handshake本质是Command,为Frame的一种。
NULL Security Mechanism机制中,以PUB/SUB模式为例,handshake的数据如下:
在这里插入图片描述
Payload内容如下:

[1 byte] Command size + [n bytes]Command Name + [1 bytes]Metadata Key size + [n bytes]Metadata Key + [4 bytes]Metadata Value size + [n bytes]Metadata Value

使用Socket实现ZMQ SUB方法

代码如下:

//
//  ZMTP 3.0 debugging subscriber
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#include <ws2tcpip.h>#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <cstdint>
#include <iostream>#pragma comment(lib, "ws2_32.lib")typedef struct
{uint8_t flags;     //  Must be zerouint8_t size;      //  Size, 0 to 255 byteuint8_t data[255]; //  Message data
} zmtp_msg_t;static void derp(char *s)
{perror(s);exit(1);
}static void tcp_send(int handle, void *buffer, size_t len)
{if (send(handle, (char *) buffer, len, 0) == -1)derp((char *) "send");
}static void tcp_recv(int handle, void *buffer, size_t len)
{printf(" - reading %d bytes: ", (int) len);fflush(stdout);size_t len_recd = 0;while (len_recd < len){size_t bytes = recv(handle, (char *) buffer + len_recd, len - len_recd, 0);if (bytes == 0)break; //  Peer has shutdownprintf(" [%d]", (int) bytes);fflush(stdout);if (bytes == -1)derp((char *) "recv");len_recd += bytes;}printf("\n");fflush(stdout);
}static void zmtp_recv(int handle, zmtp_msg_t *msg)
{tcp_recv(handle, (uint8_t *) msg, 2);tcp_recv(handle, msg->data, msg->size);
}static void zmtp_send(int handle, zmtp_msg_t *msg)
{tcp_send(handle, (uint8_t *) msg, msg->size + 2);
}//  This is the 3.0 greeting (64 bytes)
typedef struct
{uint8_t signature[10];uint8_t version[2];uint8_t mechanism[20];uint8_t as_server[1];uint8_t filler[31];
} zmtp_greeting_t;int main(void)
{puts("I: starting subscriber");WSADATA wsData;if (WSAStartup(MAKEWORD(2, 2), &wsData) != 0){std::cerr << "无法初始化Winsock" << std::endl;return 1;}//  Create TCP socketint peer;if ((peer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)derp((char *) "socket");const char *serverIP   = "127.0.0.1";const int   serverPort = 5559;sockaddr_in serverAddress {};serverAddress.sin_family = AF_INET;serverAddress.sin_port   = htons(serverPort);if (inet_pton(AF_INET, serverIP, &(serverAddress.sin_addr)) <= 0){std::cerr << "无效的服务器IP地址" << std::endl;closesocket(peer);WSACleanup();return 1;}//  Keep trying to connect until we succeedputs("I: waiting for connection");while (connect(peer, reinterpret_cast<sockaddr *>(&serverAddress), sizeof(serverAddress)) == -1)Sleep(1);puts("I: connected OK");//  This is our greeting (64 octets)zmtp_greeting_t outgoing = {{0xFF, 0, 0, 0, 0, 0, 0, 0, 1, 0x7F},{3, 0},{'N', 'U', 'L', 'L', 0},{0},{0}};//  Do full backwards version detection following RFC23//  Send first ten bytes of greeting to peertcp_send(peer, &outgoing, 10);//  Read first byte from peerzmtp_greeting_t incoming;tcp_recv(peer, &incoming, 1);uint8_t length = incoming.signature[0];if (length != 0xFF){puts("E: signature not valid (1)");closesocket(peer);exit(0);}//  Looks like 2.0+, read 9 more bytes to be suretcp_recv(peer, (uint8_t *) &incoming + 1, 9);if ((incoming.signature[9] & 1) != 1){puts("E: signature not valid (2)");closesocket(peer);exit(0);}//  Exchange major version numbersputs("I: signature valid, exchanging major versions");tcp_send(peer, (uint8_t *) &outgoing + 10, 1);tcp_recv(peer, (uint8_t *) &incoming + 10, 1);if (incoming.version[0] >= 3){//  If version >= 3, the peer is using ZMTP 3.0, so send//  rest of the greeting and continue with ZMTP 3.0.puts("I: peer is talking ZMTP 3.0");puts("I: sending rest of greeting...");tcp_send(peer, (uint8_t *) &outgoing + 11, 53);//  Get remainder of greeting from peerputs("I: waiting for greeting from peer...");tcp_recv(peer, (uint8_t *) &incoming + 11, 53);//  Do NULL handshake - send READY command//  For now, empty dictionaryputs("I: have full greeting from peer");zmtp_msg_t  ready = {0x04, 0x19};std::string data;data.push_back(0x05);data.append("READY");data.push_back(0x0b);data.append("Socket-Type");int         netByteOrderSize = htonl(3);const char *valueBytes       = reinterpret_cast<const char *>(&netByteOrderSize);data.append(valueBytes, sizeof(netByteOrderSize));data.append("SUB");memcpy(ready.data, data.c_str(), data.size());puts("I: sending READY");zmtp_send(peer, &ready);//  Now wait for peer's READY commandputs("I: expecting READY from peer");zmtp_recv(peer, &ready);//assert(memcmp(ready.data, "READY   ", 8) == 0);puts("I: OK! NULL security handshake completed");puts("I: send sub command");zmtp_msg_t subCmd {0x00, 0x01};subCmd.data[0] = 0x01;zmtp_send(peer, &subCmd);}else{puts("E: major version not valid");closesocket(peer);exit(0);}puts("I: READY, printing messages");while (true){zmtp_msg_t msg;zmtp_recv(peer, &msg);msg.data[msg.size] = 0;puts((char *) msg.data);}closesocket(peer);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64453.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始,探索C语言中的字符串

字符串 1. 前言2. 预备知识2.1 字符2.2 字符数组 3. 什么是字符串4. \04.1 \0是什么4.2 \0的作用4.2.1 打印字符串4.2.2 求字符串长度 1. 前言 大家好&#xff0c;我是努力学习游泳的鱼。你已经学会了如何使用变量和常量&#xff0c;也知道了字符的概念。但是你可能还不了解由…

BuhoCleaner for mac:让你的Mac重获新生

你是否曾经因为电脑运行缓慢而感到困扰&#xff1f;是否曾经因为大量的垃圾文件和无效的临时文件而感到头疼&#xff1f;如果你有这样的烦恼&#xff0c;那么BuhoCleaner for mac就是你的救星&#xff01; BuhoCleaner for mac是一款专门为Mac用户设计的系统清理工具&#xff…

Linux中Tomcat发布war包后无法正常访问非静态资源

事故现象 在CentOS8中安装完WEB环境&#xff0c;首次部署WEB项目DEMO案例&#xff0c;发现可以静态的网页内容&#xff0c; 但是无法向后台发送异步请求&#xff0c;全部出现404问题&#xff0c;导致数据库数据无法渲染到界面上。 原因分析 CentOS请求中提示用来获取资源的连…

跨模态可信感知

文章目录 跨模态可信感知综述摘要引言跨协议通信模式PCP网络架构 跨模态可信感知跨模态可信感知的概念跨模态可信感知的热点研究场景目前存在的挑战可能改进的方案 参考文献 跨模态可信感知综述 摘要 随着人工智能相关理论和技术的崛起&#xff0c;通信和感知领域的研究引入了…

URL重定向漏洞

URL重定向漏洞 1. URL重定向1.1. 漏洞位置 2. URL重定向基础演示2.1. 查找漏洞2.1.1. 测试漏洞2.1.2. 加载完情况2.1.3. 验证漏洞2.1.4. 成功验证 2.2. 代码修改2.2.1. 用户端代码修改2.2.2. 攻击端代码修改 2.3. 利用思路2.3.1. 用户端2.3.1.1. 验证跳转 2.3.2. 攻击端2.3.2.1…

睿趣科技:开抖音小店挣钱吗到底

在当今数字化时代&#xff0c;社交媒体平台成为了创业者们寻找商机和赚钱的新途径。而抖音作为一款风靡全球的短视频分享平台&#xff0c;自然也成为了许多人开设小店、进行创业的选择之一。那么&#xff0c;开抖音小店能否真正实现盈利&#xff0c;成为了一个备受关注的话题。…

【UE5】用法简介-使用MAWI高精度树林资产的地形材质与添加风雪效果

首先我们新建一个basic工程 然后点击floor按del键&#xff0c;把floor给删除。 只留下空白场景 点击“地形” 在这个范例里&#xff0c;我只创建一个500X500大小的地形&#xff0c;只为了告诉大家用法&#xff0c;点击创建 创建好之后有一大片空白的地形出现 让我们点左上角…

uniapp实现移动端的视频图片轮播组件

1、视频轮播组件app体验地址 https://tt.appc02.com/nesxr6 2、支持小程序、H5、APP&#xff0c;在小程序上运行的效果图 3、使用方法 第一步&#xff0c;按照截图步骤配置好 第二步&#xff1a;参考以下代码&#xff0c;使用视频图片轮播组件 <template><view>…

JavaScript -【第一周】

文章来源于网上收集和自己原创&#xff0c;若侵害到您的权利&#xff0c;请您及时联系并删除~~~ JavaScript 介绍 变量、常量、数据类型、运算符等基础概念 能够实现数据类型的转换&#xff0c;结合四则运算体会如何编程。 体会现实世界中的事物与计算机的关系理解什么是数据并…

工程制造领域:企业IT架构

一、IT组织规划架构图 1.1 IT服务保证梯队与指导思想 二、整体业务规划架构图 三、数据化项目规划架构图 四、应用系统集成架构图

Shell-AI:基于LLM实现自然语言理解的CLI工具

一、前言 随着AI技术的普及&#xff0c;部分技术领域的门槛逐步降低&#xff0c;比如非科班出身&#xff0c;非技术专业&#xff0c;甚至从未涉足技术领域&#xff0c;完全不懂服务器部署和运维&#xff0c;如今可以依托AI大模型非常轻松的掌握和使用相关技术&#xff0c;来解…

网络地址转换技术NAT(第九课)

一 什么是NAT? NAT是网络地址转换的缩写,是一种在计算机网络中使用的技术,可以将私有地址转换为公共地址,从而实现本地网络与公共网络的互联。NAT工作在网络层,可以隐藏内部网络中的IP地址和端口号,从而增强网络的安全性和灵活性。在家庭网络、企业网络、公共WIFI热点等…

MongoDB实验——MongoDB shell操作

MongoDB shell操作 实验原理 MongoDB shell是一个可执行文件&#xff0c;是MongoDB自带的一个交互式JavaScript shell&#xff0c;位于MongoDB安装路径下的/bin文件夹中。要启动MongoDB shell&#xff0c;可执行命令mongo。这将在控制台提示符中启动该shell&#xff0c;Mongo…

获取Linux内核源码

在嵌入式平台上做Linux开发的时候&#xff0c;我们用的kernel都是芯片厂家移植到自家平台上的&#xff0c;但是最初的原生Linux内核的源码是从哪里来的呢&#xff1f;下面我们介绍一下怎么获取原生的Linux源码。 从Linux社区获取内核kernel源码 Linux社区的官方网站是 https:…

构造函数和析构函数(个人学习笔记黑马学习)

构造函数:主要作用在于创建对象时为对象的成员属性赋值&#xff0c;构造函数由编译器自动调用&#xff0c;无须手动调用。析构函数:主要作用在于对象销毁前系统自动调用&#xff0c;执行一些清理工作。 #include <iostream> using namespace std;//对象初始化和清理class…

音视频开发常用工具

文章目录 前言一、VLC 播放器1、简介2、下载3、VLC media player4、VLC 打开网络串流5、VLC 作为流媒体服务器①、搭建 RTSP 流媒体服务器②、新建播放器 二、MediaInfo1、简介2、下载3、MediaInfo①、主界面②、主要功能特点③、使用方法④、Mediainfo 相关参数和含义简介 三、…

Redis 哨兵(sentinel)

1. 是什么一 1.1 吹哨人巡查监控后台master主机是否故障&#xff0c;如果故障了根据投票数自动将某一个从库转换为新主库&#xff0c;继续对外服务 1.2 作用 俗称&#xff0c;无人值守运维 哨兵的作用&#xff1a; 1、监控redis运行状态&#xff0c;包括master和slave 2、当m…

下行抢占指示 DCI format 2_1

这部分R17和R15基本内容一样&#xff0c;只是有写细节描述略有区别&#xff0c;这里就以R17版本的协议看下downlinkPreemption&#xff0c;即DCI format 2_1有关内容。 R15支持eMBB和URLLC服务。 由于URLLC业务是优先级较高的业务&#xff0c;当无线资源不足时&#xff0c;已经…

【LeetCode-中等题】200. 岛屿数量

文章目录 题目方法一&#xff1a;深度优先搜索 dfs方法二&#xff1a;广度优先搜索 bfs方法三&#xff1a;&#xff08;重点掌握&#xff09;并查集 题目 方法一&#xff1a;深度优先搜索 dfs 思路&#xff1a;让一个扫描指针扫描每一个格子&#xff0c;然后每扫到一个为1的格…

基于Gin框架的HTTP接口限速实践

在当今的微服务架构和RESTful API主导的时代&#xff0c;HTTP接口在各个业务模块之间扮演着重要的角色。随着业务规模的不断扩大&#xff0c;接口的访问频率和负载也随之增加。为了确保系统的稳定性和性能&#xff0c;接口限速成了一个重要的话题。 1 接口限速的使用场景 接口…