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 zero
    uint8_t size;      //  Size, 0 to 255 byte
    uint8_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 shutdown
        printf(" [%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 socket
    int 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 succeed
    puts("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 peer
    tcp_send(peer, &outgoing, 10);

    //  Read first byte from peer
    zmtp_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 sure
    tcp_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 numbers
    puts("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 peer
        puts("I: waiting for greeting from peer...");
        tcp_recv(peer, (uint8_t *) &incoming + 11, 53);
        //  Do NULL handshake - send READY command
        //  For now, empty dictionary
        puts("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 command
        puts("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.mfbz.cn/a/101403.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据挖掘导论学习笔记1(第1 、2章)

参考&#xff1a;https://blog.csdn.net/u013232035/article/details/48281659?spm1001.2014.3001.5506 和《数据挖掘导论》学习笔记&#xff08;第1-2章&#xff09;_时机性样本_schdut的博客-CSDN博客 第1章 绪论 数据挖掘是一种技术&#xff0c;它将传统的数据分析方法…

【配置环境】Visual Studio 配置 OpenCV

目录 一&#xff0c;环境 二&#xff0c;下载和配置 OpenCV 三&#xff0c;创建一个 Visual Studio 项目 四&#xff0c;配置 Visual Studio 项目 五&#xff0c;编写并编译 OpenCV 程序 六&#xff0c;解决CMake编译OpenCV报的错误 一&#xff0c;环境 Windows 11 家庭中…

十大管理——项目成本管理

目录 1.成本管理概念 2.成本管理的四个过程域 2.1四个过程的整体理解 ​2.2四个过程的ITO口诀版记忆 2.3过程1——制定项目管理计划 ​2.4过程2——项目成本估算​ 2.5过程3——项目成本预算 2.5过程4——项目成本控制 3计算题 1.成本管理概念 项目成本管理就是要确保…

在R中安装TensorFlow、TensorFlow_Probability、numpy(R与Python系列第二篇)

目录 前言&#xff1a; 1-安装tensorflow库 Step1: 下载R包tensorflow Step2&#xff1a;安装TensorFlow库 Step3&#xff1a;导入R中 2-安装tensorflow_probability库 Step1&#xff1a;下载R包&#xff1a;tfprobability Step2&#xff1a;安装TensorFlow Probability …

【分类】分类性能评价

评价指标 1、准确率、召回率、精确率、F-度量、ROC ​ 属于各类的样本的并不是均一分布&#xff0c;甚至其出现概率相差很多个数量级&#xff0c;这种分类问题称为不平衡类问题。在不平衡类问题中&#xff0c;准确率并没有多大意义&#xff0c;我们需要一些别的指标。 ​ 通…

Flutter:getX的学习

前言 学习教程&#xff1a;Getx教程_FlutterGetx系列实战教程 简介 getX是第三方的状态管理插件&#xff0c;不仅具有状态管理的功能&#xff0c;还具有路由管理、主题管理、国际化多语言管理、网络请求、数据验证等功能。相比其他状态管理组件&#xff0c;getX简单、功能强大…

【网络编程上】

目录 一.什么是互联网 1.计算机网络的定义与分类&#xff08;了解&#xff09; &#xff08;1&#xff09;计算机网络的定义 &#xff08;2&#xff09;计算机网络的分类 ① 按照网络的作用范围进行分类 ②按照网络的使用者进行分类 2.网络的网络 &#xff08;理解&#xf…

基于Vue3+ts+Pinia的后台管理系统

Vue3tsPinia管理系统 项目介绍项目简介界面展示登录界面商品界面用户界面角色管理界面 接口文档项目地址 项目介绍 包含对商品、订单、用户增删改查等后台的管理业务&#xff0c;并提供数据可视化的报表功能的管理系统。界面进行了高级封装&#xff0c;可以通过引入组件传入配…

利用frps搭建本地自签名https服务的透传

nginx的搭建就不介绍了&#xff0c;教程很多&#xff0c;基本上油手就会。 在本例中&#xff0c;frp服务器的域名是 www.yourfrp.com&#xff0c;同时也是反向代理nginx服务器; 本地网站要用的域名&#xff1a; test.abcd.com 请事先将 test.abcd.com 解析到 frp所在服务器…

OpenCV(十三):图像中绘制直线、圆形、椭圆形、矩形、多边形和文字

目录 1.绘制直线line() 2.绘制圆形circle() 3.绘制椭圆形ellipse() 4.绘制矩形rectangle() 5.绘制多边形 fillPoly() 6.绘制文字putText() 7.例子 1.绘制直线line() CV_EXPORTS_W void line(InputOutputArray img,Point pt1, Point pt2,const Scalar& color,int t…

CSS学习笔记05

CSS笔记05 定位 position CSS 属性position - 用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。position 有以下常用的属性值&#xff1a; position: static; - 默认值。指定元素使用正常的布局行为&am…

wap2app 隐藏系统状态栏

一、首先创建wap2App项目 1、文件》新建》项目 2、选择Wap2App项目&#xff1a;输入项目名称、网站首页地址&#xff08;如果是本地localhost的话改为你的IP地址即可&#xff09;&#xff0c;点击创建 二、创建完wap2App项目后 隐藏系统状态栏只要修改1、2选项即可 1、找到根…

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

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

aarch64-linux交叉编译libcurl带zlib和openssl

交叉编译libcurl需要依赖zlib和openssl 需要先用aarch64工具链编译zlib和openssl aarch64-linux环境搭建 下载工具链 gcc用于执行交叉编译 gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnusysroot是交叉版本的库文件集合 sysroot-glibc-linaro-2.25-2019.12-aarch64-lin…

K8S最新版本集群部署(v1.28) + 容器引擎Docker部署(下)

温故知新 &#x1f4da;第三章 Kubernetes各组件部署&#x1f4d7;安装kubectl&#xff08;可直接跳转到安装kubeadm章节&#xff0c;直接全部安装了&#xff09;&#x1f4d5;下载kubectl安装包&#x1f4d5;执行kubectl安装&#x1f4d5;验证kubectl &#x1f4d7;安装kubead…

常见矿石材质鉴定VR实训模拟操作平台提高学员的学习效果和实践能力

随着“元宇宙”概念的不断发展&#xff0c;在矿山领域中&#xff0c;长期存在传统培训内容不够丰富、教学方式单一、资源消耗大等缺点&#xff0c;无法适应当前矿山企业发展需求的长期难题。元宇宙企业借助VR虚拟现实、web3d开发和计算机技术构建的一个虚拟世界&#xff0c;为用…

F5服务器负载均衡能力如何?一文了解

但凡知道服务器负载均衡这个名词的&#xff0c;基本都知道 F5&#xff0c;因为负载均衡是 F5 的代表作&#xff0c;换句话来说&#xff0c;负载均衡就是由 F5 发明的。提到F5服务器负载均衡能力如何&#xff1f;不得不关注F5提出的关于安全、网络全面优化的解决方案&#xff0c…

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)

前期介绍 我是一名逆水寒的玩家&#xff0c;做一些游戏的笔记当作攻略记录下来&#xff0c;荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关&#xff08;非逆水寒老兵服&#xff09; &#xff08;1&#xff09;老一&#xff1a;巨鹰 1&#xff09;机制一&#xff1a;三阵风…

html中如何用vue语法,并使用UI组件库 ,html中引入vue+ant-design-vue或者vue+element-plus

html中如何用vue语法&#xff0c;并使用UI组件库 前言 先说一下本次应用的场景&#xff0c;本次项目中&#xff0c;需要引入github中别人写好的插件&#xff0c;插件比较大&#xff0c;没有方法直接在自己项目中&#xff0c;把别人的项目打包合并生成html&#xff08;类似于前…

设计模式第九讲:常见重构技巧 - 去除不必要的!=

设计模式第九讲&#xff1a;常见重构技巧 - 去除不必要的! 项目中会存在大量判空代码&#xff0c;多么丑陋繁冗&#xff01;如何避免这种情况&#xff1f;我们是否滥用了判空呢&#xff1f;本文是设计模式第九讲&#xff0c;讲解常见重构技巧&#xff1a;去除不必要的! 文章目录…