DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
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 DETWS_ENABLE_AMQP
12
13#include <string.h>
14
15// AMQP integers are big-endian (network order).
16static size_t put16(uint8_t *p, uint16_t v)
17{
18 p[0] = (uint8_t)(v >> 8);
19 p[1] = (uint8_t)v;
20 return 2;
21}
22
23static size_t put32(uint8_t *p, uint32_t v)
24{
25 p[0] = (uint8_t)(v >> 24);
26 p[1] = (uint8_t)(v >> 16);
27 p[2] = (uint8_t)(v >> 8);
28 p[3] = (uint8_t)v;
29 return 4;
30}
31
32static uint16_t get16(const uint8_t *p)
33{
34 return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
35}
36
37static uint32_t get32(const uint8_t *p)
38{
39 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
40}
41
42size_t amqp_protocol_header(uint8_t *buf, size_t cap)
43{
44 static const uint8_t hdr[8] = {'A', 'M', 'Q', 'P', 0, 0, 9, 1};
45 if (!buf || cap < sizeof(hdr))
46 return 0;
47 memcpy(buf, hdr, sizeof(hdr));
48 return sizeof(hdr);
49}
50
51// Write a frame header (type, channel, size) at buf; the caller fills the payload + 0xCE.
52static size_t write_frame_header(uint8_t *buf, uint8_t type, uint16_t channel, uint32_t size)
53{
54 size_t p = 0;
55 buf[p++] = type;
56 p += put16(buf + p, channel);
57 p += put32(buf + p, size);
58 return p; // 7
59}
60
61size_t amqp_build_frame(uint8_t *buf, size_t cap, uint8_t type, uint16_t channel, const uint8_t *payload,
62 size_t payload_len)
63{
64 if (!buf || (payload_len && !payload) || payload_len > 0xFFFFFFFFu)
65 return 0;
66 size_t total = AMQP_FRAME_OVERHEAD + payload_len;
67 if (total > cap)
68 return 0;
69 size_t p = write_frame_header(buf, type, channel, (uint32_t)payload_len);
70 if (payload_len)
71 {
72 memcpy(buf + p, payload, payload_len);
73 p += payload_len;
74 }
75 buf[p++] = AMQP_FRAME_END;
76 return p;
77}
78
79size_t amqp_build_method(uint8_t *buf, size_t cap, uint16_t channel, uint16_t class_id, uint16_t method_id,
80 const uint8_t *args, size_t args_len)
81{
82 if (!buf || (args_len && !args))
83 return 0;
84 size_t payload_len = 4 + args_len; // class-id + method-id + args
85 size_t total = AMQP_FRAME_OVERHEAD + payload_len;
86 if (total > cap)
87 return 0;
88 // Write directly into buf (no temp): header, then the method payload, then the 0xCE end.
89 size_t p = write_frame_header(buf, AMQP_FRAME_METHOD, channel, (uint32_t)payload_len);
90 p += put16(buf + p, class_id);
91 p += put16(buf + p, method_id);
92 if (args_len)
93 {
94 memcpy(buf + p, args, args_len);
95 p += args_len;
96 }
97 buf[p++] = AMQP_FRAME_END;
98 return p;
99}
100
101size_t amqp_build_heartbeat(uint8_t *buf, size_t cap)
102{
103 return amqp_build_frame(buf, cap, AMQP_FRAME_HEARTBEAT, 0, nullptr, 0);
104}
105
106bool amqp_parse_frame(const uint8_t *buf, size_t len, AmqpFrame *out, size_t *consumed)
107{
108 if (!buf || !out || len < AMQP_FRAME_OVERHEAD)
109 return false;
110 uint32_t size = get32(buf + 3);
111 // Compare against the remaining capacity without adding (a 32-bit size_t would wrap if we
112 // computed 8 + size first), so an attacker-controlled size can't slip past the bound.
113 if (size > len - AMQP_FRAME_OVERHEAD)
114 return false; // not fully buffered
115 size_t total = AMQP_FRAME_OVERHEAD + (size_t)size; // header(7) + payload + frame-end(1)
116 if (buf[7 + size] != AMQP_FRAME_END)
117 return false; // missing / corrupt frame terminator
118 out->type = buf[0];
119 out->channel = get16(buf + 1);
120 out->payload = buf + 7;
121 out->payload_len = size;
122 if (consumed)
123 *consumed = total;
124 return true;
125}
126
127bool amqp_parse_method(const uint8_t *payload, size_t payload_len, uint16_t *class_id, uint16_t *method_id,
128 const uint8_t **args, size_t *args_len)
129{
130 if (!payload || payload_len < 4)
131 return false;
132 if (class_id)
133 *class_id = get16(payload);
134 if (method_id)
135 *method_id = get16(payload + 2);
136 if (args)
137 *args = payload + 4;
138 if (args_len)
139 *args_len = payload_len - 4;
140 return true;
141}
142
143#endif // DETWS_ENABLE_AMQP
AMQP 0-9-1 frame codec (DETWS_ENABLE_AMQP) - zero-heap frame builder + parser for the RabbitMQ wire p...