Line data Source code
1 : /*
2 : ________________________________________________________________________
3 : | |
4 : | _ _ _ _ |
5 : | | (_) | | | | |
6 : | __| |_ __ _ _ __ ___ ___| |_ ___ _ __ ___ ___ __| | ___ ___ |
7 : | / _` | |/ _` | '_ ` _ \ / _ \ __/ _ \ '__/ __/ _ \ / _` |/ _ \/ __| |
8 : | | (_| | | (_| | | | | | | __/ || __/ | | (_| (_) | (_| | __/ (__ |
9 : | \__,_|_|\__,_|_| |_| |_|\___|\__\___|_| \___\___/ \__,_|\___|\___| |
10 : | |
11 : |________________________________________________________________________|
12 :
13 : C++ CODEC FOR DIAMETER PROTOCOL (RFC 6733)
14 : Version 0.0.z
15 : https://github.com/testillano/diametercodec
16 :
17 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
18 : SPDX-License-Identifier: MIT
19 : Copyright (c) 2021 Eduardo Ramos
20 :
21 : Permission is hereby granted, free of charge, to any person obtaining a copy
22 : of this software and associated documentation files (the "Software"), to deal
23 : in the Software without restriction, including without limitation the rights
24 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 : copies of the Software, and to permit persons to whom the Software is
26 : furnished to do so, subject to the following conditions:
27 :
28 : The above copyright notice and this permission notice shall be included in all
29 : copies or substantial portions of the Software.
30 :
31 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 : SOFTWARE.
38 : */
39 :
40 : // Standard
41 : #include <stdexcept>
42 :
43 : // Project
44 : #include <ert/diametercodec/codec/Message.hpp>
45 : #include <ert/diametercodec/stack/Dictionary.hpp>
46 :
47 : namespace ert {
48 : namespace diametercodec {
49 : namespace codec {
50 :
51 : // ============================================================================
52 : // Helpers (same encode/decode helpers as Avp.cpp, duplicated to keep files independent)
53 : // ============================================================================
54 :
55 : namespace {
56 :
57 15 : inline uint32_t decode4(const uint8_t* b) {
58 15 : return (uint32_t(b[0]) << 24) | (uint32_t(b[1]) << 16) | (uint32_t(b[2]) << 8) | uint32_t(b[3]);
59 : }
60 :
61 10 : inline uint32_t decode3(const uint8_t* b) { return (uint32_t(b[0]) << 16) | (uint32_t(b[1]) << 8) | uint32_t(b[2]); }
62 :
63 18 : inline void encode4(core::Buffer& out, uint32_t v) {
64 18 : out.push_back(static_cast<uint8_t>(v >> 24));
65 18 : out.push_back(static_cast<uint8_t>(v >> 16));
66 18 : out.push_back(static_cast<uint8_t>(v >> 8));
67 18 : out.push_back(static_cast<uint8_t>(v));
68 18 : }
69 :
70 6 : inline void encode3(core::Buffer& out, uint32_t v) {
71 6 : out.push_back(static_cast<uint8_t>(v >> 16));
72 6 : out.push_back(static_cast<uint8_t>(v >> 8));
73 6 : out.push_back(static_cast<uint8_t>(v));
74 6 : }
75 :
76 : } // anonymous namespace
77 :
78 : // ============================================================================
79 : // Message::decode
80 : // ============================================================================
81 7 : void Message::decode(const uint8_t* buf, size_t len, const stack::Dictionary& dict) {
82 7 : if (len < static_cast<size_t>(core::MessageHeaderLen))
83 2 : throw std::runtime_error("Not enough bytes for message header (20 bytes)");
84 :
85 : // 0 1 2 3
86 : // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
87 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 : // | Version | Message Length |
89 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 : // | command flags | Command-Code |
91 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92 : // | Application-ID |
93 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
94 : // | Hop-by-Hop Identifier |
95 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96 : // | End-to-End Identifier |
97 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98 : // | AVPs ...
99 : // +-+-+-+-+-+-+-+-+-+-+-+-+-
100 :
101 5 : version_ = buf[0];
102 5 : uint32_t msgLen = decode3(buf + 1);
103 5 : flags_ = buf[4];
104 5 : uint32_t code = decode3(buf + 5);
105 5 : id_ = core::CommandId(static_cast<core::U24>(code), isRequest());
106 5 : applicationId_ = decode4(buf + 8);
107 5 : hopByHop_ = decode4(buf + 12);
108 5 : endToEnd_ = decode4(buf + 16);
109 :
110 5 : if (len < msgLen) throw std::runtime_error("Not enough bytes to cover message length");
111 :
112 : // Decode AVPs
113 4 : avps_.clear();
114 4 : size_t pos = core::MessageHeaderLen;
115 21 : while (pos < msgLen) {
116 17 : Avp avp;
117 17 : size_t consumed = avp.decode(buf + pos, msgLen - pos, dict);
118 17 : avps_.push_back(std::move(avp));
119 17 : pos += consumed;
120 17 : }
121 4 : }
122 :
123 : // ============================================================================
124 : // Message::encode
125 : // ============================================================================
126 6 : core::Buffer Message::encode(const stack::Dictionary& dict) const {
127 6 : core::Buffer out;
128 6 : out.reserve(256); // reasonable initial capacity
129 :
130 : // Version
131 6 : out.push_back(version_);
132 :
133 : // Message Length placeholder (3 bytes)
134 6 : size_t lenPos = out.size();
135 6 : out.push_back(0);
136 6 : out.push_back(0);
137 6 : out.push_back(0);
138 :
139 : // Command Flags
140 6 : out.push_back(flags_);
141 :
142 : // Command-Code (3 bytes)
143 6 : encode3(out, id_.first);
144 :
145 : // Application-ID
146 6 : encode4(out, applicationId_);
147 :
148 : // Hop-by-Hop
149 6 : encode4(out, hopByHop_);
150 :
151 : // End-to-End
152 6 : encode4(out, endToEnd_);
153 :
154 : // AVPs
155 33 : for (const auto& avp : avps_) avp.encode(out, dict);
156 :
157 : // Write message length
158 6 : uint32_t msgLen = static_cast<uint32_t>(out.size());
159 6 : out[lenPos] = static_cast<uint8_t>(msgLen >> 16);
160 6 : out[lenPos + 1] = static_cast<uint8_t>(msgLen >> 8);
161 6 : out[lenPos + 2] = static_cast<uint8_t>(msgLen);
162 :
163 6 : return out;
164 0 : }
165 :
166 : // ============================================================================
167 : // Message::getLength
168 : // ============================================================================
169 2 : size_t Message::getLength(const stack::Dictionary& dict) const {
170 2 : size_t total = core::MessageHeaderLen;
171 7 : for (const auto& avp : avps_) total += 4 * REQUIRED_WORDS(avp.getLength(dict));
172 2 : return total;
173 : }
174 :
175 : // ============================================================================
176 : // Message::setHeaderToAnswer
177 : // ============================================================================
178 1 : void Message::setHeaderToAnswer(const Message& request) {
179 1 : version_ = request.version_;
180 1 : flags_ = request.flags_ & ~core::MsgFlagRequest; // clear R flag
181 1 : id_ = core::CommandId(request.id_.first, false);
182 1 : applicationId_ = request.applicationId_;
183 1 : hopByHop_ = request.hopByHop_;
184 1 : endToEnd_ = request.endToEnd_;
185 1 : }
186 :
187 : // ============================================================================
188 : // Message::getAvp (by id)
189 : // ============================================================================
190 4 : const Avp* Message::getAvp(const core::AvpId& id) const {
191 13 : for (const auto& avp : avps_)
192 12 : if (avp.getId() == id) return &avp;
193 1 : return nullptr;
194 : }
195 :
196 : // ============================================================================
197 : // Message::getAvp (by name)
198 : // ============================================================================
199 3 : const Avp* Message::getAvp(const std::string& name, const stack::Dictionary& dict) const {
200 3 : const stack::Avp* sa = dict.getAvp(name);
201 3 : if (!sa) return nullptr;
202 2 : return getAvp(sa->getId());
203 : }
204 :
205 : // ============================================================================
206 : // Message::toJson
207 : // ============================================================================
208 4 : nlohmann::json Message::toJson(const stack::Dictionary& dict) const {
209 4 : nlohmann::json result = nlohmann::json::object();
210 :
211 : // Header metadata
212 4 : result["_header"] = {{"version", version_},
213 4 : {"flags", flags_},
214 4 : {"command-code", id_.first},
215 0 : {"request", isRequest()},
216 4 : {"application-id", applicationId_},
217 4 : {"hop-by-hop-id", hopByHop_},
218 88 : {"end-to-end-id", endToEnd_}};
219 :
220 : // AVPs as flat JSON object (same as Grouped AVP logic)
221 26 : for (const auto& avp : avps_) {
222 22 : std::string name = avp.getName(dict);
223 22 : nlohmann::json val = avp.toJson(dict);
224 22 : if (result.contains(name)) {
225 1 : if (!result[name].is_array()) {
226 1 : nlohmann::json arr = nlohmann::json::array();
227 1 : arr.push_back(std::move(result[name]));
228 1 : result[name] = std::move(arr);
229 1 : }
230 1 : result[name].push_back(std::move(val));
231 : } else {
232 21 : result[name] = std::move(val);
233 : }
234 22 : }
235 :
236 4 : return result;
237 64 : }
238 :
239 : // ============================================================================
240 : // Message::fromJson
241 : // ============================================================================
242 15 : Message Message::fromJson(const nlohmann::json& j, const stack::Dictionary& dict) {
243 15 : Message msg;
244 :
245 : // Parse header if present
246 15 : if (j.contains("_header")) {
247 15 : const auto& h = j["_header"];
248 15 : if (h.contains("version")) msg.version_ = h["version"].get<uint8_t>();
249 15 : if (h.contains("flags")) msg.flags_ = h["flags"].get<uint8_t>();
250 15 : if (h.contains("command-code")) {
251 15 : uint32_t code = h["command-code"].get<uint32_t>();
252 15 : bool req = h.value("request", false);
253 15 : msg.id_ = core::CommandId(static_cast<core::U24>(code), req);
254 : // Set R flag accordingly
255 15 : if (req)
256 14 : msg.flags_ |= core::MsgFlagRequest;
257 : else
258 1 : msg.flags_ &= ~core::MsgFlagRequest;
259 : }
260 15 : if (h.contains("application-id")) msg.applicationId_ = h["application-id"].get<uint32_t>();
261 15 : if (h.contains("hop-by-hop-id")) msg.hopByHop_ = h["hop-by-hop-id"].get<uint32_t>();
262 15 : if (h.contains("end-to-end-id")) msg.endToEnd_ = h["end-to-end-id"].get<uint32_t>();
263 : }
264 :
265 : // Parse AVPs (all keys except _header)
266 197 : for (auto& [key, val] : j.items()) {
267 91 : if (key == "_header") continue;
268 76 : if (val.is_array()) {
269 3 : for (const auto& elem : val) msg.avps_.push_back(Avp::fromJson(key, elem, dict));
270 : } else {
271 75 : msg.avps_.push_back(Avp::fromJson(key, val, dict));
272 : }
273 15 : }
274 :
275 15 : return msg;
276 0 : }
277 :
278 : } // namespace codec
279 : } // namespace diametercodec
280 : } // namespace ert
|