DBUS源码剖析之DBusMessage消息头

DBusMessage 消息头(DBusHeader)详解

1. 概述

消息头(DBusHeader)是 D-Bus 消息的元数据部分,包含路由信息、消息类型、标志等。消息头与消息体分离存储,使用网络字节流格式,支持延迟解析和缓存优化。

1.1 消息头的组成

消息头由两部分组成:

  • 固定部分:16 字节的固定格式数据
  • 字段数组:可变长度的字段数组,每个字段包含路由信息

1.2 消息头的作用

  1. 路由:确定消息的目标和来源
  2. 类型识别:标识消息类型(METHOD_CALL、METHOD_RETURN、ERROR、SIGNAL)
  3. 匹配:通过序列号匹配请求和回复
  4. 元数据:包含消息标志、签名等信息

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_CALL
    • 2:METHOD_RETURN
    • 3:ERROR
    • 4:SIGNAL

Flags(标志)

  • 位置:偏移 2
  • 类型:byte(1 字节)
  • 标志位:
    • 0x01:NO_REPLY_EXPECTED
    • 0x02:NO_AUTO_START
    • 0x04: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 对齐规则

  1. 消息头固定部分:从偏移 0 开始,自然对齐
  2. 字段数组:从偏移 16(FIRST_FIELD_OFFSET)开始
  3. 每个字段:8 字节对齐(struct 对齐规则)
  4. 消息头末尾:填充到 8 字节边界(0-7 字节)
  5. 消息体:从 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.DBus
  • com.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.Failed
  • com.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 缓存失效

缓存会在以下情况下失效:

  1. 消息头被修改(添加/删除/修改字段)
  2. 消息头被重新初始化
  3. 消息头被复制(需要重新验证)

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 内容
  • 复制 paddingbyte_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;
    }
    // 否则继续接收数据
}
相关推荐
WaWaJie_Ngen1 天前
【操作系统】第四章---存储器管理
数据结构·算法
benben0441 天前
强化学习DQN和Actor-Critic算法
算法
爪哇部落算法小助手1 天前
每日两题day68
算法
pwn蒸鱼1 天前
buuctf中的pwn2_sctf_2016(libc泄露+栈溢出)
linux·安全
编码小哥1 天前
OpenCV角点检测:Harris与ShiTomasi算法
人工智能·opencv·算法
qq_5470261791 天前
Linux 压缩与解压缩
linux·运维·服务器
鹿角片ljp1 天前
力扣283.移动零-双指针法
算法·leetcode·排序算法
hay_lee1 天前
DeepSeek开年发布新论文:提出新型残差连接mHC
人工智能·算法·机器学习·deepseek·mhc
爱打代码的小林1 天前
机器学习(聚类算法)
算法·机器学习·聚类