DBusMessage 消息头(DBusHeader)详解
1. 概述
消息头(DBusHeader)是 D-Bus 消息的元数据部分,包含路由信息、消息类型、标志等。消息头与消息体分离存储,使用网络字节流格式,支持延迟解析和缓存优化。
1.1 消息头的组成
消息头由两部分组成:
- 固定部分:16 字节的固定格式数据
- 字段数组:可变长度的字段数组,每个字段包含路由信息
1.2 消息头的作用
- 路由:确定消息的目标和来源
- 类型识别:标识消息类型(METHOD_CALL、METHOD_RETURN、ERROR、SIGNAL)
- 匹配:通过序列号匹配请求和回复
- 元数据:包含消息标志、签名等信息
2. DBusHeader 结构
2.1 结构定义
c
struct DBusHeader
{
DBusString data; /**< 头部的网络数据,与 body 分开存储 */
DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; /**< 跟踪每个字段在头中的位置 */
dbus_uint32_t padding : 3; /**< 0-7 字节的对齐填充 */
dbus_uint32_t byte_order : 8; /**< 字节序('l' 或 'B') */
};
2.2 字段说明
2.2.1 data(网络数据)
类型 :DBusString
作用:存储消息头的网络字节流格式数据
特点:
- 与消息体(
body)分开存储 - 可以直接用于网络传输
- 支持独立重新分配内存
- 包含固定部分和字段数组
2.2.2 fields(字段缓存)
类型 :DBusHeaderField[DBUS_HEADER_FIELD_LAST + 1]
作用 :缓存每个字段在 data 中的位置
大小 :DBUS_HEADER_FIELD_LAST + 1 = 11(当前定义)
2.2.3 padding(对齐填充)
类型:位域(3 位,0-7)
作用:记录消息头末尾的对齐填充字节数
原因:消息体需要 8 字节对齐,消息头末尾可能需要填充
2.2.4 byte_order(字节序)
类型:位域(8 位)
作用:记录消息头的字节序
值:
'l':小端(little endian)'B':大端(big endian)
注意 :必须与 data[0] 的内容一致
3. 消息头网络格式
3.1 完整格式
消息头在网络上的格式(从 dbus-marshal-header.h 注释):
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | <- index % 8
|-------|-------|-------|------|-----|-----|-----|-----|
| Order | Type | Flags | Vers | Body length |
| Serial | Fields array length [A]
[A] Code |Sig.len| Signature + \0 | Content...| <- first field
| Content ... | Pad to 8-byte boundary|
| Code |Sig.len| Signature + \0 | Content... | <- second field
...
| Code |Sig.len| Signature | Content... | <- last field
| Content ... [B] Padding to 8-byte boundary [C]
[C] Body ...
...
| Body ... [D] <- no padding after natural length
3.2 固定部分(16 字节)
3.2.1 字节布局
| 偏移 | 大小 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Byte Order | 字节序('l' 或 'B') |
| 1 | 1 | Message Type | 消息类型(1-4) |
| 2 | 1 | Flags | 消息标志 |
| 3 | 1 | Protocol Version | 协议版本(当前为 1) |
| 4-7 | 4 | Body Length | 消息体长度(uint32) |
| 8-11 | 4 | Serial | 消息序列号(uint32) |
| 12-15 | 4 | Fields Array Length | 字段数组长度(uint32) |
3.2.2 字段详解
Byte Order(字节序):
- 位置:偏移 0
- 类型:
byte(1 字节) - 值:
'l'(小端)或'B'(大端) - 作用:指定后续数据的字节序
Message Type(消息类型):
- 位置:偏移 1
- 类型:
byte(1 字节) - 值:
1:METHOD_CALL2:METHOD_RETURN3:ERROR4:SIGNAL
Flags(标志):
- 位置:偏移 2
- 类型:
byte(1 字节) - 标志位:
0x01:NO_REPLY_EXPECTED0x02:NO_AUTO_START0x04:ALLOW_INTERACTIVE_AUTHORIZATION
Protocol Version(协议版本):
- 位置:偏移 3
- 类型:
byte(1 字节) - 值:当前为
1
Body Length(消息体长度):
- 位置:偏移 4-7
- 类型:
uint32(4 字节) - 说明:从 [C] 到 [D] 的距离(不包括填充)
Serial(序列号):
- 位置:偏移 8-11
- 类型:
uint32(4 字节) - 说明:消息的唯一序列号,用于匹配回复
Fields Array Length(字段数组长度):
- 位置:偏移 12-15
- 类型:
uint32(4 字节) - 说明:从 [A] 到 [B] 的距离
3.3 字段数组部分
3.3.1 字段格式
每个字段是一个 struct<byte, variant>:
| 字段代码 (1字节) | Variant 类型签名长度 (1字节) | Variant 类型签名 + \0 | Variant 值 | 填充到 8 字节边界 |
字段代码:标识字段类型(1-10)
Variant 类型签名:标识 Variant 值的类型
Variant 值:实际的字段值
对齐填充:确保下一个字段 8 字节对齐
3.3.2 字段示例
PATH 字段(假设值为 "/org/freedesktop/DBus"):
| 0x01 (PATH) | 0x01 | 'o' + \0 | "/org/freedesktop/DBus" + \0 | 填充 |
INTERFACE 字段(假设值为 "org.freedesktop.DBus"):
| 0x02 (INTERFACE) | 0x01 | 's' + \0 | "org.freedesktop.DBus" + \0 | 填充 |
REPLY_SERIAL 字段(假设值为 12345):
| 0x05 (REPLY_SERIAL) | 0x01 | 'u' + \0 | 0x00003039 (12345) | 填充 |
3.4 对齐规则
- 消息头固定部分:从偏移 0 开始,自然对齐
- 字段数组:从偏移 16(FIRST_FIELD_OFFSET)开始
- 每个字段:8 字节对齐(struct 对齐规则)
- 消息头末尾:填充到 8 字节边界(0-7 字节)
- 消息体:从 8 字节边界开始
4. 消息头字段
4.1 字段代码定义
c
#define DBUS_HEADER_FIELD_INVALID 0
#define DBUS_HEADER_FIELD_PATH 1
#define DBUS_HEADER_FIELD_INTERFACE 2
#define DBUS_HEADER_FIELD_MEMBER 3
#define DBUS_HEADER_FIELD_ERROR_NAME 4
#define DBUS_HEADER_FIELD_REPLY_SERIAL 5
#define DBUS_HEADER_FIELD_DESTINATION 6
#define DBUS_HEADER_FIELD_SENDER 7
#define DBUS_HEADER_FIELD_SIGNATURE 8
#define DBUS_HEADER_FIELD_UNIX_FDS 9
#define DBUS_HEADER_FIELD_CONTAINER_INSTANCE 10
4.2 字段详细说明
4.2.1 PATH(对象路径)
代码 :DBUS_HEADER_FIELD_PATH (1)
类型 :DBUS_TYPE_OBJECT_PATH
必需性:
- METHOD_CALL:必需
- SIGNAL:必需
- METHOD_RETURN:可选
- ERROR:可选
格式:遵循 D-Bus 对象路径规范
- 以
/开头 - 由
/分隔的组件组成 - 每个组件不能为空
- 不能以
/结尾(除非是根路径/)
示例:
/org/freedesktop/DBus/com/example/Object/
4.2.2 INTERFACE(接口名)
代码 :DBUS_HEADER_FIELD_INTERFACE (2)
类型 :DBUS_TYPE_STRING
必需性:
- METHOD_CALL:推荐(可选)
- SIGNAL:必需
- METHOD_RETURN:不需要
- ERROR:不需要
格式:遵循 D-Bus 接口名规范
- 由
.分隔的组件组成 - 每个组件不能为空
- 不能以
.开头或结尾
示例:
org.freedesktop.DBuscom.example.Interface
4.2.3 MEMBER(成员名)
代码 :DBUS_HEADER_FIELD_MEMBER (3)
类型 :DBUS_TYPE_STRING
必需性:
- METHOD_CALL:必需(方法名)
- SIGNAL:必需(信号名)
- METHOD_RETURN:不需要
- ERROR:不需要
格式:遵循 D-Bus 成员名规范
- 不能包含
. - 不能为空
- 区分大小写
示例:
Hello(方法名)NameOwnerChanged(信号名)
4.2.4 ERROR_NAME(错误名)
代码 :DBUS_HEADER_FIELD_ERROR_NAME (4)
类型 :DBUS_TYPE_STRING
必需性:
- ERROR:必需
- 其他类型:不需要
格式:遵循 D-Bus 错误名规范
- 与接口名格式相同
- 通常以
Error结尾
示例:
org.freedesktop.DBus.Error.Failedcom.example.Error.InvalidValue
4.2.5 REPLY_SERIAL(回复序列号)
代码 :DBUS_HEADER_FIELD_REPLY_SERIAL (5)
类型 :DBUS_TYPE_UINT32
必需性:
- METHOD_RETURN:必需
- ERROR:必需
- 其他类型:不需要
作用:匹配原始方法调用的序列号
示例:
c
// 方法调用序列号为 123
dbus_uint32_t call_serial = 123;
// 创建回复
DBusMessage *reply = dbus_message_new_method_return(call);
// reply 的 REPLY_SERIAL 自动设置为 123
4.2.6 DESTINATION(目标服务名)
代码 :DBUS_HEADER_FIELD_DESTINATION (6)
类型 :DBUS_TYPE_STRING
必需性:所有类型都可选
格式:遵循 D-Bus 服务名规范
- 唯一名:以
:开头(如:1.23) - 知名名:不以
:开头(如org.freedesktop.DBus)
使用场景:
- METHOD_CALL:指定目标服务
- METHOD_RETURN:自动设置为原始调用的发送者
- ERROR:自动设置为原始调用的发送者
- SIGNAL:通常不需要(广播)
4.2.7 SENDER(发送者)
代码 :DBUS_HEADER_FIELD_SENDER (7)
类型 :DBUS_TYPE_STRING
必需性:所有类型都可选(通常由消息总线自动设置)
格式 :唯一名(以 : 开头)
设置时机:
- 消息总线在发送前自动设置
- 点对点通信时可能为 NULL
4.2.8 SIGNATURE(类型签名)
代码 :DBUS_HEADER_FIELD_SIGNATURE (8)
类型 :DBUS_TYPE_SIGNATURE
必需性:所有类型都可选
作用:描述消息体的类型签名
格式:D-Bus 类型签名字符串
示例:
"s":单个字符串"is":整数 + 字符串"a{sv}":字典(字符串键,Variant 值)
4.2.9 UNIX_FDS(Unix 文件描述符数量)
代码 :DBUS_HEADER_FIELD_UNIX_FDS (9)
类型 :DBUS_TYPE_UINT32
必需性:包含 Unix fd 的消息必需
作用:指定消息中包含的 Unix 文件描述符数量
使用场景:通过消息传递文件描述符
4.2.10 CONTAINER_INSTANCE(容器实例路径)
代码 :DBUS_HEADER_FIELD_CONTAINER_INSTANCE (10)
类型 :DBUS_TYPE_OBJECT_PATH
必需性:容器化环境中的消息
作用:标识发送消息的容器实例
5. DBusHeaderField 缓存机制
5.1 概述
DBusHeaderField 缓存机制用于优化字段访问性能,避免每次访问字段时都遍历整个字段数组。
5.2 DBusHeaderField 结构
c
struct DBusHeaderField
{
int value_pos; /**< 字段值在 header->data 中的位置,或特殊值 -1/-2 */
};
value_pos 的三种状态:
| 值 | 常量 | 说明 |
|---|---|---|
| 正数 | - | 字段值在 header->data 中的字节偏移位置 |
-1 |
_DBUS_HEADER_FIELD_VALUE_UNKNOWN |
未知状态,需要重新验证缓存 |
-2 |
_DBUS_HEADER_FIELD_VALUE_NONEXISTENT |
字段不存在于消息头中 |
5.3 缓存工作原理
5.3.1 缓存数组
c
struct DBusHeader
{
DBusString data;
DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; // 11 个缓存项
// ...
};
5.3.2 缓存初始化
当消息头被创建或修改时,所有字段的缓存被标记为 UNKNOWN:
c
static void _dbus_header_cache_invalidate_all(DBusHeader *header)
{
int i;
for (i = 0; i <= DBUS_HEADER_FIELD_LAST; i++) {
header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN;
}
}
5.3.3 缓存验证
当需要访问字段时,如果缓存状态为 UNKNOWN,会触发缓存验证:
c
static void _dbus_header_cache_revalidate(DBusHeader *header)
{
// 1. 将所有字段标记为不存在
for (i = 0; i <= DBUS_HEADER_FIELD_LAST; i++) {
header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
}
// 2. 遍历消息头中的字段数组
DBusTypeReader array;
_dbus_type_reader_init(&array, ...);
_dbus_type_reader_recurse(&array, &sub);
// 3. 对于每个字段:
while (_dbus_type_reader_get_current_type(&array) != DBUS_TYPE_INVALID) {
unsigned char field_code;
_dbus_type_reader_read_basic(&sub, &field_code);
// 忽略未知字段
if (field_code > DBUS_HEADER_FIELD_LAST)
goto next_field;
// 进入 Variant,获取值的位置
_dbus_type_reader_recurse(&sub, &variant);
_dbus_header_cache_one(header, field_code, &variant);
next_field:
_dbus_type_reader_next(&array);
}
}
5.3.4 字段访问流程
c
// 1. 检查缓存状态
if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) {
// 缓存未初始化,需要验证
_dbus_header_cache_revalidate(header);
}
// 2. 检查字段是否存在
if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT) {
return FALSE; // 字段不存在
}
// 3. 使用缓存的位置直接访问字段值
int pos = header->fields[field].value_pos;
// 从 header->data 的 pos 位置读取字段值
5.4 性能优势
- 首次访问:O(n) 遍历字段数组
- 后续访问:O(1) 直接使用缓存位置
示例:
c
// 第一次访问:O(n)
const char *path1 = dbus_message_get_path(msg);
// 第二次访问:O(1)
const char *path2 = dbus_message_get_path(msg);
5.5 缓存失效
缓存会在以下情况下失效:
- 消息头被修改(添加/删除/修改字段)
- 消息头被重新初始化
- 消息头被复制(需要重新验证)
5.6 字段类型映射
每个字段代码对应一个期望的 D-Bus 类型:
c
static const HeaderFieldType _dbus_header_field_types[] = {
{ DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID },
{ DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH },
{ DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 },
{ DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE },
{ DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 },
{ DBUS_HEADER_FIELD_CONTAINER_INSTANCE, DBUS_TYPE_OBJECT_PATH }
};
6. 头字段访问函数
6.1 路径(Path)
c
// 设置路径
dbus_bool_t dbus_message_set_path(DBusMessage *message, const char *object_path);
// 获取路径
const char* dbus_message_get_path(DBusMessage *message);
// 检查路径
dbus_bool_t dbus_message_has_path(DBusMessage *message, const char *object_path);
6.2 接口(Interface)
c
// 设置接口
dbus_bool_t dbus_message_set_interface(DBusMessage *message, const char *iface);
// 获取接口
const char* dbus_message_get_interface(DBusMessage *message);
// 检查接口
dbus_bool_t dbus_message_has_interface(DBusMessage *message, const char *iface);
6.3 成员(Member)
c
// 设置成员(方法名或信号名)
dbus_bool_t dbus_message_set_member(DBusMessage *message, const char *member);
// 获取成员
const char* dbus_message_get_member(DBusMessage *message);
// 检查成员
dbus_bool_t dbus_message_has_member(DBusMessage *message, const char *member);
6.4 目标(Destination)
c
// 设置目标
dbus_bool_t dbus_message_set_destination(DBusMessage *message, const char *destination);
// 获取目标
const char* dbus_message_get_destination(DBusMessage *message);
// 检查目标
dbus_bool_t dbus_message_has_destination(DBusMessage *message, const char *bus_name);
6.5 发送者(Sender)
c
// 获取发送者(只读,通常由消息总线设置)
const char* dbus_message_get_sender(DBusMessage *message);
// 检查发送者
dbus_bool_t dbus_message_has_sender(DBusMessage *message, const char *unique_bus_name);
6.6 错误名(Error Name)
c
// 设置错误名
dbus_bool_t dbus_message_set_error_name(DBusMessage *message, const char *name);
// 获取错误名
const char* dbus_message_get_error_name(DBusMessage *message);
6.7 签名(Signature)
c
// 获取签名(只读,由消息体自动生成)
const char* dbus_message_get_signature(DBusMessage *message);
// 检查签名
dbus_bool_t dbus_message_has_signature(DBusMessage *message, const char *signature);
6.8 序列号(Serial)
c
// 获取序列号
dbus_uint32_t dbus_message_get_serial(DBusMessage *message);
// 设置序列号(通常由连接自动设置)
void dbus_message_set_serial(DBusMessage *message, dbus_uint32_t serial);
6.9 回复序列号(Reply Serial)
c
// 获取回复序列号
dbus_uint32_t dbus_message_get_reply_serial(DBusMessage *message);
// 设置回复序列号
dbus_bool_t dbus_message_set_reply_serial(DBusMessage *message, dbus_uint32_t reply_serial);
7. 消息头创建和初始化
7.1 消息头创建(_dbus_header_create)
_dbus_header_create() 是创建消息头的核心函数,它将消息头信息序列化为网络字节流格式。
7.1.1 函数签名
c
dbus_bool_t _dbus_header_create(
DBusHeader *header, // 要创建的消息头
int byte_order, // 字节序(DBUS_LITTLE_ENDIAN 或 DBUS_BIG_ENDIAN)
int message_type, // 消息类型(1-4)
const char *destination, // 目标服务名(可选)
const char *path, // 对象路径(可选)
const char *interface, // 接口名(可选)
const char *member, // 成员名(可选)
const char *error_name); // 错误名(可选,仅 ERROR 消息)
7.1.2 实现流程
完整实现代码:
c
dbus_bool_t _dbus_header_create(DBusHeader *header,
int byte_order,
int message_type,
const char *destination,
const char *path,
const char *interface,
const char *member,
const char *error_name)
{
unsigned char v_BYTE;
dbus_uint32_t v_UINT32;
DBusTypeWriter writer;
DBusTypeWriter array;
// 步骤 1: 参数验证
_dbus_assert(byte_order == DBUS_LITTLE_ENDIAN ||
byte_order == DBUS_BIG_ENDIAN);
_dbus_assert(_dbus_string_get_length(&header->data) == 0); // 必须为空
// 步骤 2: 预留填充空间
// 为消息头末尾的对齐填充预留空间(最多 7 字节)
if (!reserve_header_padding(header))
return FALSE;
// 步骤 3: 初始化类型写入器
// 使用消息头签名("yyyyuua(yv)")初始化写入器
_dbus_type_writer_init_values_only(&writer, byte_order,
&_dbus_header_signature_str, 0,
&header->data,
HEADER_END_BEFORE_PADDING(header));
// 步骤 4: 写入固定部分(16 字节)
// 4.1 字节序(偏移 0)
v_BYTE = byte_order;
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_BYTE, &v_BYTE))
goto oom;
// 4.2 消息类型(偏移 1)
v_BYTE = message_type;
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_BYTE, &v_BYTE))
goto oom;
// 4.3 标志(偏移 2)
v_BYTE = 0; // 初始标志为 0
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_BYTE, &v_BYTE))
goto oom;
// 4.4 协议版本(偏移 3)
v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION; // 当前为 1
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_BYTE, &v_BYTE))
goto oom;
// 4.5 消息体长度(偏移 4-7)
v_UINT32 = 0; // 初始为 0,稍后更新
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_UINT32, &v_UINT32))
goto oom;
// 4.6 序列号(偏移 8-11)
v_UINT32 = 0; // 初始为 0,发送时分配
if (!_dbus_type_writer_write_basic(&writer, DBUS_TYPE_UINT32, &v_UINT32))
goto oom;
// 步骤 5: 进入字段数组
// 字段数组的类型是 array of struct(byte, variant)
if (!_dbus_type_writer_recurse(&writer, DBUS_TYPE_ARRAY,
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&array))
goto oom;
// 步骤 6: 写入字段(按特定顺序)
// 6.1 PATH 字段(如果提供)
if (path != NULL) {
if (!write_basic_field(&array,
DBUS_HEADER_FIELD_PATH,
DBUS_TYPE_OBJECT_PATH,
&path))
goto oom;
}
// 6.2 DESTINATION 字段(如果提供)
if (destination != NULL) {
if (!write_basic_field(&array,
DBUS_HEADER_FIELD_DESTINATION,
DBUS_TYPE_STRING,
&destination))
goto oom;
}
// 6.3 INTERFACE 字段(如果提供)
// 注意:测试代码依赖此顺序,不要随意更改
if (interface != NULL) {
if (!write_basic_field(&array,
DBUS_HEADER_FIELD_INTERFACE,
DBUS_TYPE_STRING,
&interface))
goto oom;
}
// 6.4 MEMBER 字段(如果提供)
if (member != NULL) {
if (!write_basic_field(&array,
DBUS_HEADER_FIELD_MEMBER,
DBUS_TYPE_STRING,
&member))
goto oom;
}
// 6.5 ERROR_NAME 字段(如果提供,仅 ERROR 消息)
if (error_name != NULL) {
if (!write_basic_field(&array,
DBUS_HEADER_FIELD_ERROR_NAME,
DBUS_TYPE_STRING,
&error_name))
goto oom;
}
// 步骤 7: 退出字段数组
if (!_dbus_type_writer_unrecurse(&writer, &array))
goto oom;
// 步骤 8: 修正填充
// 计算并设置消息头末尾的对齐填充(0-7 字节)
correct_header_padding(header);
return TRUE;
oom:
// 错误处理:清理已写入的数据
_dbus_string_delete(&header->data, 0,
_dbus_string_get_length(&header->data) - header->padding);
correct_header_padding(header);
return FALSE;
}
7.1.3 字段写入函数(write_basic_field)
每个字段的写入通过 write_basic_field() 完成:
c
static dbus_bool_t write_basic_field(DBusTypeWriter *writer,
int field,
int type,
const void *value)
{
DBusTypeWriter sub;
DBusTypeWriter variant;
unsigned char field_byte;
// 1. 进入 struct(byte, variant)
_dbus_type_writer_recurse(writer, DBUS_TYPE_STRUCT, NULL, &sub);
// 2. 写入字段代码(byte)
field_byte = field;
_dbus_type_writer_write_basic(&sub, DBUS_TYPE_BYTE, &field_byte);
// 3. 进入 variant
_dbus_type_writer_recurse(&sub, DBUS_TYPE_VARIANT, type_signature, &variant);
// 4. 写入字段值(根据类型)
_dbus_type_writer_write_basic(&variant, type, value);
// 5. 退出 variant 和 struct
_dbus_type_writer_unrecurse(&sub, &variant);
_dbus_type_writer_unrecurse(writer, &sub);
return TRUE;
}
字段格式:
- 每个字段是一个
struct<byte, variant> byte:字段代码(1-10)variant:字段值,包含类型签名和实际值
7.1.4 内存布局示例
假设调用 _dbus_header_create(header, 'l', 1, "com.example", "/obj", "com.example.IFace", "Method", NULL):
header->data 内容(小端字节序):
[偏移 0-15] 固定部分:
0: 'l' (字节序)
1: 1 (METHOD_CALL)
2: 0 (标志)
3: 1 (协议版本)
4-7: 0 (消息体长度)
8-11: 0 (序列号)
12-15: 字段数组长度(稍后填充)
[偏移 16+] 字段数组:
字段 1 (PATH):
16: 0x01 (字段代码)
17: 0x01 (Variant 签名长度)
18: 'o' + \0 (Variant 类型签名)
20: 4 (字符串长度,uint32)
24: "/obj" + \0 (值)
29-31: 填充到 8 字节边界
字段 6 (DESTINATION):
32: 0x06 (字段代码)
33: 0x01 (Variant 签名长度)
34: 's' + \0 (Variant 类型签名)
36: 11 (字符串长度,uint32)
40: "com.example" + \0 (值)
52-55: 填充到 8 字节边界
字段 2 (INTERFACE):
56: 0x02 (字段代码)
...
字段 3 (MEMBER):
...
[消息头末尾] 对齐填充(0-7 字节)
7.2 消息头初始化
7.2.1 _dbus_header_init
功能:初始化消息头结构,分配内存但不创建内容
c
dbus_bool_t _dbus_header_init(DBusHeader *header)
{
// 预分配 32 字节缓冲区(足够存储最小消息头)
if (!_dbus_string_init_preallocated(&header->data, 32))
return FALSE;
// 重新初始化(清空并重置状态)
_dbus_header_reinit(header);
return TRUE;
}
使用场景:
- 创建新消息时
- 消息头尚未初始化时
返回值:
TRUE:成功FALSE:内存不足
7.2.2 _dbus_header_reinit
功能:重新初始化已初始化的消息头(清空内容,重置状态)
c
void _dbus_header_reinit(DBusHeader *header)
{
// 清空数据(但保留缓冲区)
_dbus_string_set_length(&header->data, 0);
// 重置填充
header->padding = 0;
// 使所有字段缓存失效
_dbus_header_cache_invalidate_all(header);
}
使用场景:
- 消息缓存重用(从缓存获取的消息需要重新初始化)
- 重置消息头状态
特点:
- 不清空缓冲区,避免内存分配
- 重置所有状态(填充、缓存等)
7.2.3 _dbus_header_free
功能:释放消息头占用的内存
c
void _dbus_header_free(DBusHeader *header)
{
// 释放 DBusString 占用的内存
_dbus_string_free(&header->data);
}
使用场景:
- 消息销毁时
- 错误处理时清理资源
注意:
- 只释放
data字段的内存 fields数组是栈分配的,无需释放
7.3 消息头复制
c
dbus_bool_t _dbus_header_copy(const DBusHeader *header,
DBusHeader *dest)
功能:复制消息头到目标结构
实现要点:
- 复制
data内容 - 复制
padding和byte_order - 复制字段缓存(但需要重新验证)
- 重置序列号为 0
使用场景:
- 消息复制时
- 消息克隆时
8. 消息头验证
8.1 长度验证
消息头必须至少包含 16 字节的固定部分:
c
#define DBUS_MINIMUM_HEADER_SIZE 16
验证逻辑:
c
if (len < DBUS_MINIMUM_HEADER_SIZE) {
// 数据不足,无法解析消息头
return FALSE;
}
8.2 完整性验证(_dbus_header_have_message_untrusted)
这是验证消息头完整性的核心函数,用于检查是否有足够的数据来解析一个完整的消息。
8.2.1 函数签名
c
dbus_bool_t _dbus_header_have_message_untrusted(
int max_message_length, // 最大消息长度限制
DBusValidity *validity, // 输出:验证结果
int *byte_order, // 输出:字节序
int *fields_array_len, // 输出:字段数组长度
int *header_len, // 输出:消息头长度
int *body_len, // 输出:消息体长度
const DBusString *str, // 输入数据
int start, // 起始位置(必须 8 字节对齐)
int len); // 数据长度
8.2.2 实现流程
完整实现代码:
c
dbus_bool_t _dbus_header_have_message_untrusted(
int max_message_length,
DBusValidity *validity,
int *byte_order,
int *fields_array_len,
int *header_len,
int *body_len,
const DBusString *str,
int start,
int len)
{
dbus_uint32_t header_len_unsigned;
dbus_uint32_t fields_array_len_unsigned;
dbus_uint32_t body_len_unsigned;
// 步骤 1: 基本检查
_dbus_assert(start >= 0);
_dbus_assert(start < _DBUS_INT32_MAX / 2);
_dbus_assert(len >= 0);
_dbus_assert(start == (int)_DBUS_ALIGN_VALUE(start, 8)); // 必须 8 字节对齐
// 步骤 2: 检查最小长度
if (len < DBUS_MINIMUM_HEADER_SIZE) {
*validity = DBUS_VALID;
return FALSE; // 数据不足,但格式可能有效
}
// 步骤 3: 读取字节序(偏移 0)
*byte_order = _dbus_string_get_byte(str, start + BYTE_ORDER_OFFSET);
// 步骤 4: 验证字节序
if (*byte_order != DBUS_LITTLE_ENDIAN &&
*byte_order != DBUS_BIG_ENDIAN) {
*validity = DBUS_INVALID_BAD_BYTE_ORDER;
return FALSE; // 无效字节序
}
// 步骤 5: 读取字段数组长度(偏移 12-15)
_dbus_assert(FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len);
fields_array_len_unsigned = _dbus_marshal_read_uint32(
str, start + FIELDS_ARRAY_LENGTH_OFFSET,
*byte_order, NULL);
// 步骤 6: 验证字段数组长度合理性
if (fields_array_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH;
return FALSE; // 字段数组长度异常(可能被攻击)
}
// 步骤 7: 读取消息体长度(偏移 4-7)
_dbus_assert(BODY_LENGTH_OFFSET + 4 < len);
body_len_unsigned = _dbus_marshal_read_uint32(
str, start + BODY_LENGTH_OFFSET,
*byte_order, NULL);
// 步骤 8: 验证消息体长度合理性
if (body_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_INSANE_BODY_LENGTH;
return FALSE; // 消息体长度异常(可能被攻击)
}
// 步骤 9: 计算消息头长度
// 消息头长度 = 固定部分(16) + 字段数组长度 + 对齐填充
header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned;
header_len_unsigned = _DBUS_ALIGN_VALUE(header_len_unsigned, 8);
// 步骤 10: 验证总长度合理性
// 防止整数溢出和异常大的消息
_dbus_assert(max_message_length < _DBUS_INT32_MAX / 2);
if (body_len_unsigned + header_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_MESSAGE_TOO_LONG;
return FALSE; // 消息总长度超过限制
}
// 步骤 11: 类型转换和范围检查
_dbus_assert(body_len_unsigned < (unsigned)_DBUS_INT32_MAX);
_dbus_assert(fields_array_len_unsigned < (unsigned)_DBUS_INT32_MAX);
_dbus_assert(header_len_unsigned < (unsigned)_DBUS_INT32_MAX);
// 步骤 12: 设置输出参数
*body_len = body_len_unsigned;
*fields_array_len = fields_array_len_unsigned;
*header_len = header_len_unsigned;
*validity = DBUS_VALID;
// 步骤 13: 检查是否有足够的数据
// 需要的数据 = 消息头长度 + 消息体长度
_dbus_verbose("have %d bytes, need body %u + header %u = %u\n",
len, body_len_unsigned, header_len_unsigned,
body_len_unsigned + header_len_unsigned);
return (body_len_unsigned + header_len_unsigned) <= (unsigned)len;
}
8.2.3 验证检查项
1. 字节序验证:
c
if (*byte_order != DBUS_LITTLE_ENDIAN &&
*byte_order != DBUS_BIG_ENDIAN) {
*validity = DBUS_INVALID_BAD_BYTE_ORDER;
return FALSE;
}
- 检查字节序是否为有效值('l' 或 'B')
- 无效字节序表示数据损坏或攻击
2. 字段数组长度验证:
c
if (fields_array_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH;
return FALSE;
}
- 防止异常大的字段数组(DoS 攻击)
- 字段数组长度不应超过最大消息长度
3. 消息体长度验证:
c
if (body_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_INSANE_BODY_LENGTH;
return FALSE;
}
- 防止异常大的消息体(DoS 攻击)
- 消息体长度不应超过最大消息长度
4. 总长度验证:
c
if (body_len_unsigned + header_len_unsigned > (unsigned)max_message_length) {
*validity = DBUS_INVALID_MESSAGE_TOO_LONG;
return FALSE;
}
- 防止整数溢出
- 确保消息总长度在合理范围内
5. 数据完整性检查:
c
return (body_len_unsigned + header_len_unsigned) <= (unsigned)len;
- 检查是否有足够的数据来解析完整消息
- 如果数据不足,返回
FALSE(但validity仍为DBUS_VALID)
8.3 字段验证
8.3.1 必需字段检查(check_mandatory_fields)
c
static DBusValidity check_mandatory_fields(DBusHeader *header)
{
switch (_dbus_header_get_message_type(header)) {
case DBUS_MESSAGE_TYPE_SIGNAL:
REQUIRE_FIELD(INTERFACE); // 信号必须有接口
/* FALL THRU */
case DBUS_MESSAGE_TYPE_METHOD_CALL:
REQUIRE_FIELD(PATH); // 方法调用必须有路径
REQUIRE_FIELD(MEMBER); // 方法调用必须有成员
break;
case DBUS_MESSAGE_TYPE_ERROR:
REQUIRE_FIELD(ERROR_NAME); // 错误消息必须有错误名
REQUIRE_FIELD(REPLY_SERIAL); // 错误消息必须有回复序列号
break;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
REQUIRE_FIELD(REPLY_SERIAL); // 方法返回必须有回复序列号
break;
default:
break;
}
return DBUS_VALID;
}
REQUIRE_FIELD 宏:
c
#define REQUIRE_FIELD(name) \
do { \
if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) \
return DBUS_INVALID_MISSING_##name; \
} while (0)
8.3.2 字段类型验证(load_and_validate_field)
c
static DBusValidity load_and_validate_field(DBusHeader *header,
int field,
DBusTypeReader *variant_reader)
{
int type;
int expected_type;
// 1. 获取字段的实际类型
type = _dbus_type_reader_get_current_type(variant_reader);
// 2. 获取期望的类型
expected_type = EXPECTED_TYPE_OF_FIELD(field);
// 3. 验证类型匹配
if (type != expected_type) {
return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE;
}
// 4. 检查字段是否重复
if (header->fields[field].value_pos >= 0) {
return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE;
}
// 5. 缓存字段位置
_dbus_header_cache_one(header, field, variant_reader);
// 6. 根据字段类型进行特定验证
switch (field) {
case DBUS_HEADER_FIELD_DESTINATION:
// 验证服务名格式
if (!_dbus_validate_bus_name(value_str, str_data_pos, len))
return DBUS_INVALID_BAD_DESTINATION;
break;
case DBUS_HEADER_FIELD_INTERFACE:
// 验证接口名格式
if (!_dbus_validate_interface(value_str, str_data_pos, len))
return DBUS_INVALID_BAD_INTERFACE;
// 检查是否使用本地接口(不允许)
if (is_local_interface(value_str, str_data_pos))
return DBUS_INVALID_USES_LOCAL_INTERFACE;
break;
case DBUS_HEADER_FIELD_MEMBER:
// 验证成员名格式
if (!_dbus_validate_member(value_str, str_data_pos, len))
return DBUS_INVALID_BAD_MEMBER;
break;
case DBUS_HEADER_FIELD_REPLY_SERIAL:
// 序列号不能为 0
if (v_UINT32 == 0)
return DBUS_INVALID_BAD_SERIAL;
break;
// ... 其他字段的验证
}
return DBUS_VALID;
}
8.3.3 验证错误码
| 错误码 | 说明 |
|---|---|
DBUS_INVALID_BAD_BYTE_ORDER |
无效字节序 |
DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH |
字段数组长度异常 |
DBUS_INVALID_INSANE_BODY_LENGTH |
消息体长度异常 |
DBUS_INVALID_MESSAGE_TOO_LONG |
消息总长度超过限制 |
DBUS_INVALID_MISSING_PATH |
缺少 PATH 字段 |
DBUS_INVALID_MISSING_MEMBER |
缺少 MEMBER 字段 |
DBUS_INVALID_MISSING_INTERFACE |
缺少 INTERFACE 字段 |
DBUS_INVALID_MISSING_ERROR_NAME |
缺少 ERROR_NAME 字段 |
DBUS_INVALID_MISSING_REPLY_SERIAL |
缺少 REPLY_SERIAL 字段 |
DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE |
字段类型错误 |
DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE |
字段重复出现 |
DBUS_INVALID_BAD_DESTINATION |
无效的目标服务名 |
DBUS_INVALID_BAD_INTERFACE |
无效的接口名 |
DBUS_INVALID_BAD_MEMBER |
无效的成员名 |
DBUS_INVALID_BAD_SERIAL |
无效的序列号(为 0) |
DBUS_INVALID_USES_LOCAL_INTERFACE |
使用了本地接口(不允许) |
DBUS_INVALID_USES_LOCAL_PATH |
使用了本地路径(不允许) |
8.4 使用示例
8.4.1 检查消息完整性
c
DBusString data; // 接收到的数据
int start = 0; // 起始位置(8 字节对齐)
int len = _dbus_string_get_length(&data);
DBusValidity validity;
int byte_order;
int fields_array_len;
int header_len;
int body_len;
// 检查是否有完整的消息
if (_dbus_header_have_message_untrusted(
max_message_size, // 例如:64 * 1024
&validity,
&byte_order,
&fields_array_len,
&header_len,
&body_len,
&data,
start,
len)) {
// 有完整的消息
printf("Message complete: header=%d, body=%d\n", header_len, body_len);
} else {
if (validity != DBUS_VALID) {
// 数据无效
fprintf(stderr, "Invalid message: %s\n",
_dbus_validity_to_error_message(validity));
} else {
// 数据不足,需要等待更多数据
printf("Need more data\n");
}
}
8.4.2 流式接收消息
c
char buffer[4096];
int received = 0;
int start = 0;
while (1) {
// 接收数据
int n = read(socket_fd, buffer + received, sizeof(buffer) - received);
if (n <= 0) break;
received += n;
// 检查是否有完整消息
DBusValidity validity;
int byte_order, fields_array_len, header_len, body_len;
if (_dbus_header_have_message_untrusted(
max_message_size,
&validity,
&byte_order,
&fields_array_len,
&header_len,
&body_len,
&data_string, // 包装 buffer 的 DBusString
start,
received)) {
// 有完整消息,解析它
int total_len = header_len + body_len;
DBusMessage *msg = parse_message(buffer + start, total_len);
// 处理消息
process_message(msg);
dbus_message_unref(msg);
// 移除已处理的数据
memmove(buffer, buffer + start + total_len, received - start - total_len);
received -= (start + total_len);
start = 0;
} else if (validity != DBUS_VALID) {
// 数据无效,丢弃
fprintf(stderr, "Invalid message, discarding\n");
received = 0;
start = 0;
}
// 否则继续接收数据
}