ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
amqp.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file amqp.cpp
6 * @brief AMQP 0-9-1 frame builder + parser (pure, host-tested).
7 */
8
10
11#if PC_ENABLE_AMQP
12
13#include <string.h>
14
16
17size_t pc_amqp_protocol_header(uint8_t *buf, size_t cap)
18{
19 static const uint8_t hdr[8] = {'A', 'M', 'Q', 'P', 0, 0, 9, 1};
20 if (!buf || cap < sizeof(hdr))
21 {
22 return 0;
23 }
24 memcpy(buf, hdr, sizeof(hdr));
25 return sizeof(hdr);
26}
27
28// Write a frame header (type, channel, size) at buf; the caller fills the payload + 0xCE.
29static size_t write_frame_header(uint8_t *buf, uint8_t type, uint16_t channel, uint32_t size)
30{
31 size_t p = 0;
32 buf[p++] = type;
33 p += pc_wr16be(buf + p, channel);
34 p += pc_wr32be(buf + p, size);
35 return p; // 7
36}
37
38size_t pc_amqp_build_frame(uint8_t *buf, size_t cap, uint8_t type, uint16_t channel, const uint8_t *payload,
39 size_t payload_len)
40{
41 if (!buf || (payload_len && !payload) || payload_len > 0xFFFFFFFFu)
42 {
43 return 0;
44 }
45 size_t total = AMQP_FRAME_OVERHEAD + payload_len;
46 if (total > cap)
47 {
48 return 0;
49 }
50 size_t p = write_frame_header(buf, type, channel, (uint32_t)payload_len);
51 if (payload_len)
52 {
53 memcpy(buf + p, payload, payload_len);
54 p += payload_len;
55 }
56 buf[p++] = AMQP_FRAME_END;
57 return p;
58}
59
60size_t pc_amqp_build_method(uint8_t *buf, size_t cap, uint16_t channel, uint16_t class_id, uint16_t method_id,
61 const uint8_t *args, size_t args_len)
62{
63 if (!buf || (args_len && !args))
64 {
65 return 0;
66 }
67 size_t payload_len = 4 + args_len; // class-id + method-id + args
68 size_t total = AMQP_FRAME_OVERHEAD + payload_len;
69 if (total > cap)
70 {
71 return 0;
72 }
73 // Write directly into buf (no temp): header, then the method payload, then the 0xCE end.
74 size_t p = write_frame_header(buf, AMQP_FRAME_METHOD, channel, (uint32_t)payload_len);
75 p += pc_wr16be(buf + p, class_id);
76 p += pc_wr16be(buf + p, method_id);
77 if (args_len)
78 {
79 memcpy(buf + p, args, args_len);
80 p += args_len;
81 }
82 buf[p++] = AMQP_FRAME_END;
83 return p;
84}
85
86size_t pc_amqp_build_content_header(uint8_t *buf, size_t cap, uint16_t channel, uint16_t class_id, uint64_t body_size,
87 uint16_t property_flags, const uint8_t *properties, size_t properties_len)
88{
89 if (!buf || (properties_len && !properties))
90 {
91 return 0;
92 }
93 size_t payload_len = 2 + 2 + 8 + 2 + properties_len; // class-id + weight + body-size + property-flags + props
94 size_t total = AMQP_FRAME_OVERHEAD + payload_len;
95 if (total > cap)
96 {
97 return 0;
98 }
99 size_t p = write_frame_header(buf, AMQP_FRAME_HEADER, channel, (uint32_t)payload_len);
100 p += pc_wr16be(buf + p, class_id);
101 p += pc_wr16be(buf + p, 0); // weight (deprecated, always 0)
102 p += pc_wr64be(buf + p, body_size);
103 p += pc_wr16be(buf + p, property_flags);
104 if (properties_len)
105 {
106 memcpy(buf + p, properties, properties_len);
107 p += properties_len;
108 }
109 buf[p++] = AMQP_FRAME_END;
110 return p;
111}
112
113size_t pc_amqp_build_heartbeat(uint8_t *buf, size_t cap)
114{
115 return pc_amqp_build_frame(buf, cap, AMQP_FRAME_HEARTBEAT, 0, nullptr, 0);
116}
117
118bool pc_amqp_parse_frame(const uint8_t *buf, size_t len, AmqpFrame *out, size_t *consumed)
119{
120 if (!buf || !out || len < AMQP_FRAME_OVERHEAD)
121 {
122 return false;
123 }
124 uint32_t size = pc_rd32be(buf + 3);
125 // Compare against the remaining capacity without adding (a 32-bit size_t would wrap if we
126 // computed 8 + size first), so an attacker-controlled size can't slip past the bound.
127 if (size > len - AMQP_FRAME_OVERHEAD)
128 {
129 return false; // not fully buffered
130 }
131 size_t total = AMQP_FRAME_OVERHEAD + (size_t)size; // header(7) + payload + frame-end(1)
132 if (buf[7 + size] != AMQP_FRAME_END)
133 {
134 return false; // missing / corrupt frame terminator
135 }
136 out->type = buf[0];
137 out->channel = pc_rd16be(buf + 1);
138 out->payload = buf + 7;
139 out->payload_len = size;
140 if (consumed)
141 {
142 *consumed = total;
143 }
144 return true;
145}
146
147bool pc_amqp_parse_method(const uint8_t *payload, size_t payload_len, uint16_t *class_id, uint16_t *method_id,
148 const uint8_t **args, size_t *args_len)
149{
150 if (!payload || payload_len < 4)
151 {
152 return false;
153 }
154 if (class_id)
155 {
156 *class_id = pc_rd16be(payload);
157 }
158 if (method_id)
159 {
160 *method_id = pc_rd16be(payload + 2);
161 }
162 if (args)
163 {
164 *args = payload + 4;
165 }
166 if (args_len)
167 {
168 *args_len = payload_len - 4;
169 }
170 return true;
171}
172
173#endif // PC_ENABLE_AMQP
AMQP 0-9-1 frame codec (PC_ENABLE_AMQP) - zero-heap frame builder + parser for the RabbitMQ wire prot...
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
size_t pc_wr64be(uint8_t *p, uint64_t v)
Write v big-endian at p.
Definition endian.h:103
uint32_t pc_rd32be(const uint8_t *p)
Read a big-endian u32 at p.
Definition endian.h:119
size_t pc_wr16be(uint8_t *p, uint16_t v)
Write v big-endian at p.
Definition endian.h:85
uint16_t pc_rd16be(const uint8_t *p)
Read a big-endian u16 at p.
Definition endian.h:113
size_t pc_wr32be(uint8_t *p, uint32_t v)
Write v big-endian at p.
Definition endian.h:93