LCOV - code coverage report
Current view: top level - codec - Avp.cpp (source / functions) Coverage Total Hit
Test: final-coverage.info Lines: 85.0 % 379 322
Test Date: 2026-08-11 23:06:37 Functions: 96.0 % 25 24

            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
        

Generated by: LCOV version 2.0-1