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 <arpa/inet.h>
42 :
43 : #include <cctype>
44 : #include <cstring>
45 : #include <iomanip>
46 : #include <sstream>
47 :
48 : // Project
49 : #include <ert/diametercodec/codec/Avp.hpp>
50 : #include <ert/diametercodec/stack/Avp.hpp>
51 : #include <ert/diametercodec/stack/Dictionary.hpp>
52 : #include <ert/diametercodec/stack/Format.hpp>
53 : #include <ert/tracing/Logger.hpp>
54 :
55 : namespace ert {
56 : namespace diametercodec {
57 : namespace codec {
58 :
59 : // ============================================================================
60 : // Helpers: network byte order encode/decode
61 : // ============================================================================
62 :
63 : namespace {
64 :
65 118 : inline uint32_t decode4(const uint8_t* b) {
66 118 : return (uint32_t(b[0]) << 24) | (uint32_t(b[1]) << 16) | (uint32_t(b[2]) << 8) | uint32_t(b[3]);
67 : }
68 :
69 91 : inline uint32_t decode3(const uint8_t* b) { return (uint32_t(b[0]) << 16) | (uint32_t(b[1]) << 8) | uint32_t(b[2]); }
70 :
71 9 : inline uint16_t decode2(const uint8_t* b) { return (uint16_t(b[0]) << 8) | uint16_t(b[1]); }
72 :
73 10 : inline int32_t decodeSigned4(const uint8_t* b) {
74 10 : uint32_t u = decode4(b);
75 : int32_t v;
76 10 : std::memcpy(&v, &u, 4);
77 10 : return v;
78 : }
79 :
80 3 : inline int64_t decodeSigned8(const uint8_t* b) {
81 3 : uint64_t u = (uint64_t(b[0]) << 56) | (uint64_t(b[1]) << 48) | (uint64_t(b[2]) << 40) | (uint64_t(b[3]) << 32) |
82 3 : (uint64_t(b[4]) << 24) | (uint64_t(b[5]) << 16) | (uint64_t(b[6]) << 8) | uint64_t(b[7]);
83 : int64_t v;
84 3 : std::memcpy(&v, &u, 8);
85 3 : return v;
86 : }
87 :
88 7 : inline uint64_t decodeUnsigned8(const uint8_t* b) {
89 7 : return (uint64_t(b[0]) << 56) | (uint64_t(b[1]) << 48) | (uint64_t(b[2]) << 40) | (uint64_t(b[3]) << 32) |
90 7 : (uint64_t(b[4]) << 24) | (uint64_t(b[5]) << 16) | (uint64_t(b[6]) << 8) | uint64_t(b[7]);
91 : }
92 :
93 94 : inline void encode4(core::Buffer& out, uint32_t v) {
94 94 : out.push_back(static_cast<uint8_t>(v >> 24));
95 94 : out.push_back(static_cast<uint8_t>(v >> 16));
96 94 : out.push_back(static_cast<uint8_t>(v >> 8));
97 94 : out.push_back(static_cast<uint8_t>(v));
98 94 : }
99 :
100 : inline void encode3(core::Buffer& out, uint32_t v) {
101 : out.push_back(static_cast<uint8_t>(v >> 16));
102 : out.push_back(static_cast<uint8_t>(v >> 8));
103 : out.push_back(static_cast<uint8_t>(v));
104 : }
105 :
106 9 : inline void encode2(core::Buffer& out, uint16_t v) {
107 9 : out.push_back(static_cast<uint8_t>(v >> 8));
108 9 : out.push_back(static_cast<uint8_t>(v));
109 9 : }
110 :
111 7 : inline void encode8(core::Buffer& out, uint64_t v) {
112 63 : for (int i = 56; i >= 0; i -= 8) out.push_back(static_cast<uint8_t>(v >> i));
113 7 : }
114 :
115 73 : inline void encodePadding(core::Buffer& out, size_t dataLen) {
116 73 : size_t pad = (4 - (dataLen % 4)) % 4;
117 143 : for (size_t i = 0; i < pad; ++i) out.push_back(0);
118 73 : }
119 :
120 : // Resolve the format type for an AVP using the dictionary.
121 : // Returns the direct type (e.g., UTF8String, Address, Time) rather than
122 : // recursing to the parent basic type. This allows the codec to handle
123 : // derived types with specialized encode/decode/json logic.
124 228 : stack::Format::Type::_v resolveFormatType(const core::AvpId& id, const stack::Dictionary& dict) {
125 228 : const stack::Avp* sa = dict.getAvp(id);
126 228 : if (!sa) return stack::Format::Type::Unknown;
127 225 : const stack::Format* fmt = sa->getFormat();
128 225 : if (!fmt || fmt->isReserved()) return stack::Format::Type::Unknown;
129 225 : return stack::Format::Type::asEnum(fmt->getName());
130 : }
131 :
132 : // Hex string helpers
133 2 : std::string toHex(const uint8_t* data, size_t len) {
134 2 : std::ostringstream oss;
135 2 : oss << std::hex << std::setfill('0');
136 10 : for (size_t i = 0; i < len; ++i) oss << std::setw(2) << static_cast<unsigned>(data[i]);
137 4 : return oss.str();
138 2 : }
139 :
140 3 : core::Buffer fromHex(const std::string& hex) {
141 3 : if (hex.size() % 2 != 0) throw std::runtime_error("Invalid hex string (odd number of digits): '" + hex + "'");
142 3 : core::Buffer buf;
143 3 : buf.reserve(hex.size() / 2);
144 15 : for (size_t i = 0; i < hex.size(); i += 2) {
145 24 : if (!std::isxdigit(static_cast<unsigned char>(hex[i])) ||
146 12 : !std::isxdigit(static_cast<unsigned char>(hex[i + 1])))
147 0 : throw std::runtime_error("Invalid hex string (non-hex digit): '" + hex + "'");
148 12 : uint8_t byte = static_cast<uint8_t>(std::stoul(hex.substr(i, 2), nullptr, 16));
149 12 : buf.push_back(byte);
150 : }
151 3 : return buf;
152 0 : }
153 :
154 : // Address helpers
155 9 : std::string decodeAddress(const uint8_t* buf, size_t len) {
156 9 : if (len < 2) throw std::runtime_error("Address AVP too short");
157 9 : uint16_t family = decode2(buf);
158 : char str[INET6_ADDRSTRLEN];
159 :
160 9 : if (family == 1 && len == 6) { // IPv4
161 18 : if (inet_ntop(AF_INET, buf + 2, str, sizeof(str))) return str;
162 0 : throw std::runtime_error("Failed to decode IPv4 address");
163 : }
164 3 : if (family == 2 && len == 18) { // IPv6
165 9 : if (inet_ntop(AF_INET6, buf + 2, str, sizeof(str))) return str;
166 0 : throw std::runtime_error("Failed to decode IPv6 address");
167 : }
168 : // Unknown family: return as hex
169 0 : return toHex(buf, len);
170 : }
171 :
172 9 : void encodeAddress(core::Buffer& out, const std::string& addr) {
173 : uint8_t buf4[4], buf6[16];
174 9 : if (inet_pton(AF_INET, addr.c_str(), buf4) == 1) {
175 7 : encode2(out, 1); // IPv4 family
176 7 : out.insert(out.end(), buf4, buf4 + 4);
177 2 : } else if (inet_pton(AF_INET6, addr.c_str(), buf6) == 1) {
178 2 : encode2(out, 2); // IPv6 family
179 2 : out.insert(out.end(), buf6, buf6 + 16);
180 : } else {
181 0 : throw std::runtime_error("Invalid IP address: " + addr);
182 : }
183 9 : }
184 :
185 : } // anonymous namespace
186 :
187 : // Forward declaration (defined below, before Avp::toJson)
188 : static std::string avpValueTrace(const core::AvpId& id, const Avp::Data& data, const stack::Dictionary& dict);
189 :
190 : // ============================================================================
191 : // Avp::decode
192 : // ============================================================================
193 93 : size_t Avp::decode(const uint8_t* buf, size_t len, const stack::Dictionary& dict) {
194 93 : if (len < static_cast<size_t>(core::AvpHeaderLenWithoutVendor))
195 2 : throw std::runtime_error("Not enough bytes for AVP header");
196 :
197 : // AVP Code (4 bytes)
198 91 : uint32_t code = decode4(buf);
199 : // Flags (1 byte)
200 91 : flags_ = buf[4];
201 : // AVP Length (3 bytes) — includes header, excludes padding
202 91 : uint32_t avpLen = decode3(buf + 5);
203 :
204 : // Vendor-ID
205 91 : uint32_t vendorId = 0;
206 91 : int headerLen = core::AvpHeaderLenWithoutVendor;
207 91 : if (vendorBit()) {
208 3 : headerLen = core::AvpHeaderLenWithVendor;
209 3 : if (len < static_cast<size_t>(headerLen))
210 1 : throw std::runtime_error("Not enough bytes for AVP header with vendor");
211 2 : vendorId = decode4(buf + 8);
212 : }
213 :
214 90 : id_ = core::AvpId(static_cast<core::S32>(code), static_cast<core::S32>(vendorId));
215 :
216 90 : if (avpLen < static_cast<uint32_t>(headerLen)) throw std::runtime_error("AVP length smaller than header");
217 :
218 89 : uint32_t dataLen = avpLen - headerLen;
219 :
220 89 : if (len < avpLen) throw std::runtime_error("Not enough bytes for AVP data");
221 :
222 : // Decode data part
223 88 : decodeData(buf + headerLen, dataLen, dict);
224 :
225 : // Return total consumed bytes (padded to 4-byte boundary)
226 83 : return 4 * REQUIRED_WORDS(avpLen);
227 : }
228 :
229 : // ============================================================================
230 : // Avp::decodeData
231 : // ============================================================================
232 88 : void Avp::decodeData(const uint8_t* buf, size_t dataLen, const stack::Dictionary& dict) {
233 88 : auto basicType = resolveFormatType(id_, dict);
234 :
235 88 : switch (basicType) {
236 11 : case stack::Format::Type::Integer32:
237 : case stack::Format::Type::Enumerated:
238 11 : if (dataLen != 4) throw std::runtime_error("Integer32/Enumerated must be 4 bytes");
239 10 : data_ = decodeSigned4(buf);
240 10 : break;
241 :
242 3 : case stack::Format::Type::Integer64:
243 3 : if (dataLen != 8) throw std::runtime_error("Integer64 must be 8 bytes");
244 3 : data_ = decodeSigned8(buf);
245 3 : break;
246 :
247 8 : case stack::Format::Type::Unsigned32:
248 8 : if (dataLen != 4) throw std::runtime_error("Unsigned32 must be 4 bytes");
249 8 : data_ = decode4(buf);
250 8 : break;
251 :
252 4 : case stack::Format::Type::Unsigned64:
253 4 : if (dataLen != 8) throw std::runtime_error("Unsigned64 must be 8 bytes");
254 3 : data_ = decodeUnsigned8(buf);
255 3 : break;
256 :
257 5 : case stack::Format::Type::Float32: {
258 5 : if (dataLen != 4) throw std::runtime_error("Float32 must be 4 bytes");
259 4 : uint32_t u = decode4(buf);
260 : float f;
261 4 : std::memcpy(&f, &u, 4);
262 4 : data_ = f;
263 4 : break;
264 : }
265 :
266 5 : case stack::Format::Type::Float64: {
267 5 : if (dataLen != 8) throw std::runtime_error("Float64 must be 8 bytes");
268 4 : uint64_t u = decodeUnsigned8(buf);
269 : double d;
270 4 : std::memcpy(&d, &u, 8);
271 4 : data_ = d;
272 4 : break;
273 : }
274 :
275 9 : case stack::Format::Type::Address:
276 9 : data_ = decodeAddress(buf, dataLen);
277 9 : break;
278 :
279 4 : case stack::Format::Type::Time:
280 4 : if (dataLen != 4) throw std::runtime_error("Time must be 4 bytes");
281 3 : data_ = decode4(buf); // stored as uint32_t (NTP timestamp)
282 3 : break;
283 :
284 3 : case stack::Format::Type::Grouped: {
285 3 : std::vector<Avp> children;
286 3 : size_t pos = 0;
287 9 : while (pos < dataLen) {
288 6 : Avp child;
289 6 : size_t consumed = child.decode(buf + pos, dataLen - pos, dict);
290 6 : children.push_back(std::move(child));
291 6 : pos += consumed;
292 6 : }
293 3 : data_ = std::move(children);
294 3 : break;
295 3 : }
296 :
297 : // OctetString, UTF8String, DiameterIdentity, DiameterURI, IPFilterRule, QoSFilterRule, Unknown
298 36 : case stack::Format::Type::OctetString:
299 : case stack::Format::Type::UTF8String:
300 : case stack::Format::Type::DiameterIdentity:
301 : case stack::Format::Type::DiameterURI:
302 : case stack::Format::Type::IPFilterRule:
303 : case stack::Format::Type::QoSFilterRule:
304 : default:
305 36 : data_ = std::string(reinterpret_cast<const char*>(buf), dataLen);
306 36 : break;
307 : }
308 :
309 83 : LOGDEBUG(ert::tracing::Logger::debug(
310 : ert::tracing::Logger::asString("Diameter decode AVP '%s' (%s): 0x%s -> %s", getName(dict).c_str(),
311 : stack::Format::Type::asText(basicType), toHex(buf, dataLen).c_str(),
312 : avpValueTrace(id_, data_, dict).c_str()),
313 : ERT_FILE_LOCATION));
314 83 : }
315 :
316 : // ============================================================================
317 : // Avp::encode
318 : // ============================================================================
319 73 : void Avp::encode(core::Buffer& out, const stack::Dictionary& dict) const {
320 : // AVP Code
321 73 : encode4(out, static_cast<uint32_t>(id_.first));
322 :
323 : // Flags
324 73 : out.push_back(flags_);
325 :
326 : // Length placeholder (3 bytes) — filled after encoding data
327 73 : size_t lenPos = out.size();
328 73 : out.push_back(0);
329 73 : out.push_back(0);
330 73 : out.push_back(0);
331 :
332 : // Vendor-ID
333 73 : if (vendorBit()) {
334 1 : encode4(out, static_cast<uint32_t>(id_.second));
335 : }
336 :
337 : // Data part
338 73 : size_t dataStart = out.size();
339 73 : encodeData(out, dict);
340 73 : size_t dataEnd = out.size();
341 :
342 : // Compute and write AVP length (header + data, no padding)
343 73 : int headerLen = vendorBit() ? core::AvpHeaderLenWithVendor : core::AvpHeaderLenWithoutVendor;
344 73 : uint32_t avpLen = headerLen + static_cast<uint32_t>(dataEnd - dataStart);
345 73 : out[lenPos] = static_cast<uint8_t>(avpLen >> 16);
346 73 : out[lenPos + 1] = static_cast<uint8_t>(avpLen >> 8);
347 73 : out[lenPos + 2] = static_cast<uint8_t>(avpLen);
348 :
349 : // Padding to 4-byte boundary
350 73 : encodePadding(out, avpLen);
351 73 : }
352 :
353 : // ============================================================================
354 : // Avp::encodeData
355 : // ============================================================================
356 73 : size_t Avp::encodeData(core::Buffer& out, const stack::Dictionary& dict) const {
357 73 : size_t start = out.size();
358 73 : auto basicType = resolveFormatType(id_, dict);
359 :
360 73 : switch (basicType) {
361 7 : case stack::Format::Type::Integer32:
362 : case stack::Format::Type::Enumerated: {
363 7 : int32_t v = std::get<int32_t>(data_);
364 : uint32_t u;
365 7 : std::memcpy(&u, &v, 4);
366 7 : encode4(out, u);
367 7 : break;
368 : }
369 :
370 2 : case stack::Format::Type::Integer64: {
371 2 : int64_t v = std::get<int64_t>(data_);
372 : uint64_t u;
373 2 : std::memcpy(&u, &v, 8);
374 2 : encode8(out, u);
375 2 : break;
376 : }
377 :
378 8 : case stack::Format::Type::Unsigned32:
379 8 : encode4(out, std::get<uint32_t>(data_));
380 8 : break;
381 :
382 2 : case stack::Format::Type::Unsigned64:
383 2 : encode8(out, std::get<uint64_t>(data_));
384 2 : break;
385 :
386 3 : case stack::Format::Type::Float32: {
387 3 : float f = std::get<float>(data_);
388 : uint32_t u;
389 3 : std::memcpy(&u, &f, 4);
390 3 : encode4(out, u);
391 3 : break;
392 : }
393 :
394 3 : case stack::Format::Type::Float64: {
395 3 : double d = std::get<double>(data_);
396 : uint64_t u;
397 3 : std::memcpy(&u, &d, 8);
398 3 : encode8(out, u);
399 3 : break;
400 : }
401 :
402 9 : case stack::Format::Type::Address:
403 9 : encodeAddress(out, std::get<std::string>(data_));
404 9 : break;
405 :
406 2 : case stack::Format::Type::Time:
407 2 : encode4(out, std::get<uint32_t>(data_));
408 2 : break;
409 :
410 2 : case stack::Format::Type::Grouped: {
411 2 : const auto& children = std::get<std::vector<Avp>>(data_);
412 6 : for (const auto& child : children) child.encode(out, dict);
413 2 : break;
414 : }
415 :
416 : // OctetString and all string-based types, including Unknown
417 35 : case stack::Format::Type::OctetString:
418 : case stack::Format::Type::UTF8String:
419 : case stack::Format::Type::DiameterIdentity:
420 : case stack::Format::Type::DiameterURI:
421 : case stack::Format::Type::IPFilterRule:
422 : case stack::Format::Type::QoSFilterRule:
423 : default: {
424 35 : const auto& s = std::get<std::string>(data_);
425 35 : out.insert(out.end(), reinterpret_cast<const uint8_t*>(s.data()),
426 35 : reinterpret_cast<const uint8_t*>(s.data()) + s.size());
427 35 : break;
428 : }
429 : }
430 :
431 73 : LOGDEBUG(ert::tracing::Logger::debug(
432 : ert::tracing::Logger::asString("Diameter encode AVP '%s' (%s): %s -> 0x%s", getName(dict).c_str(),
433 : stack::Format::Type::asText(basicType), avpValueTrace(id_, data_, dict).c_str(),
434 : toHex(out.data() + start, out.size() - start).c_str()),
435 : ERT_FILE_LOCATION));
436 :
437 73 : return out.size() - start;
438 : }
439 :
440 : // ============================================================================
441 : // Avp::getLength
442 : // ============================================================================
443 9 : size_t Avp::getLength(const stack::Dictionary& dict) const {
444 9 : int headerLen = vendorBit() ? core::AvpHeaderLenWithVendor : core::AvpHeaderLenWithoutVendor;
445 9 : auto basicType = resolveFormatType(id_, dict);
446 :
447 9 : switch (basicType) {
448 2 : case stack::Format::Type::Integer32:
449 : case stack::Format::Type::Enumerated:
450 : case stack::Format::Type::Unsigned32:
451 : case stack::Format::Type::Float32:
452 : case stack::Format::Type::Time:
453 2 : return headerLen + 4;
454 :
455 0 : case stack::Format::Type::Integer64:
456 : case stack::Format::Type::Unsigned64:
457 : case stack::Format::Type::Float64:
458 0 : return headerLen + 8;
459 :
460 3 : case stack::Format::Type::Address: {
461 3 : const auto& addr = std::get<std::string>(data_);
462 : uint8_t buf4[4];
463 3 : if (inet_pton(AF_INET, addr.c_str(), buf4) == 1) return headerLen + 6; // 2 family + 4 addr
464 1 : return headerLen + 18; // 2 family + 16 addr
465 : }
466 :
467 0 : case stack::Format::Type::Grouped: {
468 0 : size_t total = headerLen;
469 0 : const auto& children = std::get<std::vector<Avp>>(data_);
470 0 : for (const auto& child : children) total += 4 * REQUIRED_WORDS(child.getLength(dict));
471 0 : return total;
472 : }
473 :
474 : // OctetString and all string-based types
475 4 : case stack::Format::Type::OctetString:
476 : case stack::Format::Type::UTF8String:
477 : case stack::Format::Type::DiameterIdentity:
478 : case stack::Format::Type::DiameterURI:
479 : case stack::Format::Type::IPFilterRule:
480 : case stack::Format::Type::QoSFilterRule:
481 : default:
482 4 : return headerLen + std::get<std::string>(data_).size();
483 : }
484 : }
485 :
486 : // ============================================================================
487 : // Avp::getName
488 : // ============================================================================
489 29 : std::string Avp::getName(const stack::Dictionary& dict) const {
490 29 : const stack::Avp* sa = dict.getAvp(id_);
491 29 : if (sa) return sa->getName();
492 : // Unknown AVP: use code-vendor format
493 2 : std::ostringstream oss;
494 2 : oss << "avp-" << id_.first;
495 2 : if (id_.second != 0) oss << "-" << id_.second;
496 2 : return oss.str();
497 2 : }
498 :
499 : // ============================================================================
500 : // avpValueTrace: human-readable rendering of an AVP value for debug traces.
501 : // For Enumerated it appends the dictionary alias/literal when available.
502 : // ============================================================================
503 0 : static std::string avpValueTrace(const core::AvpId& id, const Avp::Data& data, const stack::Dictionary& dict) {
504 0 : auto t = resolveFormatType(id, dict);
505 0 : switch (t) {
506 0 : case stack::Format::Type::Integer32:
507 0 : return std::to_string(std::get<int32_t>(data));
508 0 : case stack::Format::Type::Enumerated: {
509 0 : int32_t v = std::get<int32_t>(data);
510 0 : std::string s = std::to_string(v);
511 0 : const stack::Avp* sa = dict.getAvp(id);
512 0 : const char* alias = sa ? sa->getAlias(std::to_string(v)) : nullptr;
513 0 : if (alias) {
514 0 : s += " (";
515 0 : s += alias;
516 0 : s += ")";
517 : }
518 0 : return s;
519 0 : }
520 0 : case stack::Format::Type::Integer64:
521 0 : return std::to_string(std::get<int64_t>(data));
522 0 : case stack::Format::Type::Unsigned32:
523 0 : return std::to_string(std::get<uint32_t>(data));
524 0 : case stack::Format::Type::Unsigned64:
525 0 : return std::to_string(std::get<uint64_t>(data));
526 0 : case stack::Format::Type::Float32:
527 0 : return std::to_string(std::get<float>(data));
528 0 : case stack::Format::Type::Float64:
529 0 : return std::to_string(std::get<double>(data));
530 0 : case stack::Format::Type::Address:
531 0 : return std::get<std::string>(data);
532 0 : case stack::Format::Type::Time:
533 0 : return std::to_string(std::get<uint32_t>(data) - core::NtpEpochOffset) + " (epoch)";
534 0 : case stack::Format::Type::Grouped:
535 0 : return "<grouped>";
536 0 : case stack::Format::Type::UTF8String:
537 : case stack::Format::Type::DiameterIdentity:
538 : case stack::Format::Type::DiameterURI:
539 : case stack::Format::Type::IPFilterRule:
540 : case stack::Format::Type::QoSFilterRule:
541 0 : return "\"" + std::get<std::string>(data) + "\"";
542 0 : default: { // OctetString / Unknown
543 0 : const auto& s = std::get<std::string>(data);
544 0 : return "0x" + toHex(reinterpret_cast<const uint8_t*>(s.data()), s.size());
545 : }
546 : }
547 : }
548 :
549 : // ============================================================================
550 : // Avp::toJson
551 : // ============================================================================
552 58 : nlohmann::json Avp::toJson(const stack::Dictionary& dict) const { return dataToJson(dict); }
553 :
554 58 : nlohmann::json Avp::dataToJson(const stack::Dictionary& dict) const {
555 58 : auto basicType = resolveFormatType(id_, dict);
556 :
557 58 : switch (basicType) {
558 6 : case stack::Format::Type::Integer32:
559 : case stack::Format::Type::Enumerated:
560 6 : return std::get<int32_t>(data_);
561 :
562 2 : case stack::Format::Type::Integer64:
563 2 : return std::get<int64_t>(data_);
564 :
565 6 : case stack::Format::Type::Unsigned32:
566 6 : return std::get<uint32_t>(data_);
567 :
568 2 : case stack::Format::Type::Unsigned64:
569 2 : return std::get<uint64_t>(data_);
570 :
571 2 : case stack::Format::Type::Float32:
572 2 : return std::get<float>(data_);
573 :
574 2 : case stack::Format::Type::Float64:
575 2 : return std::get<double>(data_);
576 :
577 8 : case stack::Format::Type::Address:
578 8 : return std::get<std::string>(data_);
579 :
580 2 : case stack::Format::Type::Time:
581 : // Convert NTP to UNIX epoch
582 2 : return std::get<uint32_t>(data_) - core::NtpEpochOffset;
583 :
584 2 : case stack::Format::Type::Grouped: {
585 2 : nlohmann::json obj = nlohmann::json::object();
586 2 : const auto& children = std::get<std::vector<Avp>>(data_);
587 6 : for (const auto& child : children) {
588 4 : std::string name = child.getName(dict);
589 4 : nlohmann::json val = child.toJson(dict);
590 4 : if (obj.contains(name)) {
591 : // Convert to array for repeated AVPs
592 0 : if (!obj[name].is_array()) {
593 0 : nlohmann::json arr = nlohmann::json::array();
594 0 : arr.push_back(std::move(obj[name]));
595 0 : obj[name] = std::move(arr);
596 0 : }
597 0 : obj[name].push_back(std::move(val));
598 : } else {
599 4 : obj[name] = std::move(val);
600 : }
601 4 : }
602 2 : return obj;
603 2 : }
604 :
605 24 : case stack::Format::Type::UTF8String:
606 : case stack::Format::Type::DiameterIdentity:
607 : case stack::Format::Type::DiameterURI:
608 : case stack::Format::Type::IPFilterRule:
609 : case stack::Format::Type::QoSFilterRule:
610 24 : return std::get<std::string>(data_);
611 :
612 : // OctetString or unknown: hex representation
613 2 : default: {
614 2 : const auto& s = std::get<std::string>(data_);
615 2 : return toHex(reinterpret_cast<const uint8_t*>(s.data()), s.size());
616 : }
617 : }
618 : }
619 :
620 : // ============================================================================
621 : // Avp::fromJson
622 : // ============================================================================
623 121 : Avp Avp::fromJson(const std::string& avpName, const nlohmann::json& value, const stack::Dictionary& dict) {
624 121 : Avp avp;
625 :
626 : // Resolve AVP id from name
627 121 : const stack::Avp* sa = dict.getAvp(avpName);
628 121 : if (!sa) throw std::runtime_error("Unknown AVP name: " + avpName);
629 :
630 120 : avp.id_ = sa->getId();
631 :
632 : // Set flags from dictionary
633 120 : avp.flags_ = 0;
634 120 : if (sa->vBit()) avp.flags_ |= core::AvpFlagVendor;
635 120 : if (sa->mBit()) avp.flags_ |= core::AvpFlagMandatory;
636 :
637 120 : const stack::Format* fmt = sa->getFormat();
638 120 : if (!fmt || fmt->isReserved()) throw std::runtime_error("No format for AVP: " + avpName);
639 :
640 120 : auto basicType = stack::Format::Type::asEnum(fmt->getName());
641 :
642 120 : switch (basicType) {
643 6 : case stack::Format::Type::Integer32:
644 : case stack::Format::Type::Enumerated:
645 6 : avp.data_ = value.get<int32_t>();
646 6 : break;
647 :
648 2 : case stack::Format::Type::Integer64:
649 2 : avp.data_ = value.get<int64_t>();
650 2 : break;
651 :
652 20 : case stack::Format::Type::Unsigned32:
653 20 : avp.data_ = value.get<uint32_t>();
654 20 : break;
655 :
656 2 : case stack::Format::Type::Unsigned64:
657 2 : avp.data_ = value.get<uint64_t>();
658 2 : break;
659 :
660 2 : case stack::Format::Type::Float32:
661 2 : avp.data_ = value.get<float>();
662 2 : break;
663 :
664 2 : case stack::Format::Type::Float64:
665 2 : avp.data_ = value.get<double>();
666 2 : break;
667 :
668 21 : case stack::Format::Type::Address:
669 21 : avp.data_ = value.get<std::string>();
670 21 : break;
671 :
672 2 : case stack::Format::Type::Time:
673 : // JSON has UNIX epoch, convert to NTP
674 2 : avp.data_ = value.get<uint32_t>() + core::NtpEpochOffset;
675 2 : break;
676 :
677 2 : case stack::Format::Type::Grouped: {
678 2 : std::vector<Avp> children;
679 6 : for (auto& [key, val] : value.items()) {
680 4 : if (val.is_array()) {
681 0 : for (const auto& elem : val) children.push_back(Avp::fromJson(key, elem, dict));
682 : } else {
683 4 : children.push_back(Avp::fromJson(key, val, dict));
684 : }
685 2 : }
686 2 : avp.data_ = std::move(children);
687 2 : break;
688 2 : }
689 :
690 58 : case stack::Format::Type::UTF8String:
691 : case stack::Format::Type::DiameterIdentity:
692 : case stack::Format::Type::DiameterURI:
693 : case stack::Format::Type::IPFilterRule:
694 : case stack::Format::Type::QoSFilterRule:
695 58 : avp.data_ = value.get<std::string>();
696 58 : break;
697 :
698 : // OctetString: hex string → raw bytes
699 3 : default: {
700 3 : auto hex = value.get<std::string>();
701 3 : auto raw = fromHex(hex);
702 3 : avp.data_ = std::string(reinterpret_cast<const char*>(raw.data()), raw.size());
703 3 : break;
704 3 : }
705 : }
706 :
707 120 : return avp;
708 1 : }
709 :
710 : } // namespace codec
711 : } // namespace diametercodec
712 : } // namespace ert
|