LCOV - code coverage report
Current view: top level - codec - Message.cpp (source / functions) Coverage Total Hit
Test: final-coverage.info Lines: 97.5 % 122 119
Test Date: 2026-08-11 23:06:37 Functions: 100.0 % 12 12

            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
        

Generated by: LCOV version 2.0-1