Line data Source code
1 : #include <iostream>
2 : /*
3 : ________________________________________________________________________
4 : | |
5 : | _ _ _ _ |
6 : | | (_) | | | | |
7 : | __| |_ __ _ _ __ ___ ___| |_ ___ _ __ ___ ___ __| | ___ ___ |
8 : | / _` | |/ _` | '_ ` _ \ / _ \ __/ _ \ '__/ __/ _ \ / _` |/ _ \/ __| |
9 : | | (_| | | (_| | | | | | | __/ || __/ | | (_| (_) | (_| | __/ (__ |
10 : | \__,_|_|\__,_|_| |_| |_|\___|\__\___|_| \___\___/ \__,_|\___|\___| |
11 : | |
12 : |________________________________________________________________________|
13 :
14 : C++ CODEC FOR DIAMETER PROTOCOL (RFC 6733)
15 : Version 0.0.z
16 : https://github.com/testillano/diametercodec
17 :
18 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
19 : SPDX-License-Identifier: MIT
20 : Copyright (c) 2021 Eduardo Ramos
21 :
22 : Permission is hereby granted, free of charge, to any person obtaining a copy
23 : of this software and associated documentation files (the "Software"), to deal
24 : in the Software without restriction, including without limitation the rights
25 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26 : copies of the Software, and to permit persons to whom the Software is
27 : furnished to do so, subject to the following conditions:
28 :
29 : The above copyright notice and this permission notice shall be included in all
30 : copies or substantial portions of the Software.
31 :
32 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38 : SOFTWARE.
39 : */
40 :
41 : // Project
42 : #include <ert/diametercodec/stack/Dictionary.hpp>
43 : #include <ert/tracing/Logger.hpp>
44 :
45 : namespace ert {
46 : namespace diametercodec {
47 : namespace stack {
48 :
49 146 : void Dictionary::initialize() {
50 146 : formats_.clear();
51 146 : vendors_.clear();
52 146 : avps_.clear();
53 146 : commands_.clear();
54 146 : vendor_names_.clear();
55 146 : avp_names_.clear();
56 146 : command_names_.clear();
57 :
58 : // RFC6733 Diameter Formats harcoding:
59 : // Basic diameter types
60 146 : Format OctetString(this), Integer32(this), Integer64(this), Unsigned32(this), Unsigned64(this), Float32(this),
61 146 : Float64(this), Grouped(this);
62 146 : OctetString.setType(Format::Type::OctetString);
63 146 : Integer32.setType(Format::Type::Integer32);
64 146 : Integer64.setType(Format::Type::Integer64);
65 146 : Unsigned32.setType(Format::Type::Unsigned32);
66 146 : Unsigned64.setType(Format::Type::Unsigned64);
67 146 : Float32.setType(Format::Type::Float32);
68 146 : Float64.setType(Format::Type::Float64);
69 146 : Grouped.setType(Format::Type::Grouped);
70 :
71 146 : addFormat(OctetString);
72 146 : addFormat(Integer32);
73 146 : addFormat(Integer64);
74 146 : addFormat(Unsigned32);
75 146 : addFormat(Unsigned64);
76 146 : addFormat(Float32);
77 146 : addFormat(Float64);
78 146 : addFormat(Grouped);
79 :
80 : // Derived diameter types
81 146 : Format Address(this), Time(this), UTF8String(this), DiameterIdentity(this), DiameterURI(this), Enumerated(this),
82 146 : IPFilterRule(this), QoSFilterRule(this);
83 146 : Address.setType(Format::Type::Address);
84 146 : Address.setParentName(OctetString.getName());
85 146 : Time.setType(Format::Type::Time);
86 146 : Time.setParentName(OctetString.getName());
87 146 : UTF8String.setType(Format::Type::UTF8String);
88 146 : UTF8String.setParentName(OctetString.getName());
89 146 : DiameterIdentity.setType(Format::Type::DiameterIdentity);
90 146 : DiameterIdentity.setParentName(OctetString.getName());
91 146 : DiameterURI.setType(Format::Type::DiameterURI);
92 146 : DiameterURI.setParentName(OctetString.getName());
93 146 : Enumerated.setType(Format::Type::Enumerated);
94 146 : Enumerated.setParentName(Integer32.getName());
95 146 : IPFilterRule.setType(Format::Type::IPFilterRule);
96 146 : IPFilterRule.setParentName(OctetString.getName());
97 146 : QoSFilterRule.setType(Format::Type::QoSFilterRule);
98 146 : QoSFilterRule.setParentName(OctetString.getName());
99 :
100 146 : addFormat(Address);
101 146 : addFormat(Time);
102 146 : addFormat(UTF8String);
103 146 : addFormat(DiameterIdentity);
104 146 : addFormat(DiameterURI);
105 146 : addFormat(Enumerated);
106 146 : addFormat(IPFilterRule);
107 146 : addFormat(QoSFilterRule);
108 :
109 : // Generic AVP format:
110 146 : Format Any(this);
111 146 : Any.setType(Format::Type::Any);
112 146 : addFormat(Any, true /*reserved*/);
113 :
114 : // Generic AVP:
115 146 : Avp genericAvp(this);
116 146 : genericAvp.setCode(0);
117 146 : genericAvp.setVendorId(0 /*Vendor::Code::Ietf*/);
118 146 : genericAvp.setName("AVP");
119 146 : genericAvp.setFormatName(Any.getName());
120 146 : genericAvp.setVBit(false);
121 146 : genericAvp.setMBit(false);
122 146 : addAvp(genericAvp);
123 146 : }
124 :
125 2482 : void Dictionary::addFormat(const Format &format, bool reserved) {
126 2482 : if (!reserved && format.isReserved()) {
127 : std::string s_ex =
128 0 : ert::tracing::Logger::asString("Format type '%s' is reserved for internal use", format.getName().c_str());
129 0 : throw std::runtime_error(s_ex);
130 0 : }
131 :
132 2482 : const Format *found = getFormat(format.getName());
133 2482 : if (found) {
134 : // Update:
135 0 : LOGINFORMATIONAL(std::string trace = "Updated format '"; trace += format.getName(); trace += "'";
136 : ert::tracing::Logger::informational(trace, ERT_FILE_LOCATION););
137 : }
138 :
139 2482 : formats_[format.getName()] = format;
140 2482 : }
141 :
142 876 : void Dictionary::addVendor(const Vendor &vendor) {
143 876 : vendors_[vendor.getCode()] = vendor;
144 876 : vendor_names_[vendor.getName()] = getVendor(vendor.getCode());
145 876 : }
146 :
147 15792 : void Dictionary::addAvp(const Avp &avp) {
148 15792 : avps_[avp.getId()] = avp;
149 15792 : avp_names_[avp.getName()] = getAvp(avp.getId());
150 15792 : }
151 :
152 2044 : void Dictionary::addCommand(const Command &command) {
153 2044 : commands_[command.getId()] = command;
154 2044 : command_names_[command.getName()] = getCommand(command.getId());
155 2044 : }
156 :
157 450507 : const Format *Dictionary::getFormat(const std::string &formatName) const {
158 450507 : auto it = formats_.find(formatName);
159 :
160 450507 : if (it != formats_.end()) return &(it->second);
161 :
162 2482 : return (nullptr);
163 : }
164 :
165 876 : const Vendor *Dictionary::getVendor(core::S32 vendorId) const {
166 876 : auto it = vendors_.find(vendorId);
167 :
168 876 : if (it != vendors_.end()) return &(it->second);
169 :
170 0 : return (nullptr);
171 : }
172 :
173 0 : const Vendor *Dictionary::getVendor(const std::string &vendorName) const {
174 0 : auto it = vendor_names_.find(vendorName);
175 :
176 0 : if (it != vendor_names_.end()) return (it->second);
177 :
178 0 : return (nullptr);
179 : }
180 :
181 16054 : Avp *Dictionary::getAvp(const core::AvpId &avpId) const {
182 16054 : auto it = avps_.find(avpId);
183 :
184 16054 : if (it != avps_.end()) return (Avp *)&(it->second);
185 :
186 5 : return (nullptr);
187 : }
188 :
189 33559 : Avp *Dictionary::getAvp(const std::string &avpName) const {
190 33559 : auto it = avp_names_.find(avpName);
191 :
192 33559 : if (it != avp_names_.end()) return (Avp *)(it->second);
193 :
194 2 : return (nullptr);
195 : }
196 :
197 2044 : Command *Dictionary::getCommand(const core::CommandId &commandId) const {
198 2044 : auto it = commands_.find(commandId);
199 :
200 2044 : if (it != commands_.end()) return (Command *)&(it->second);
201 :
202 0 : return (nullptr);
203 : }
204 :
205 0 : Command *Dictionary::getCommand(const std::string &commandName) const {
206 0 : auto it = command_names_.find(commandName);
207 :
208 0 : if (it != command_names_.end()) return (Command *)(it->second);
209 :
210 0 : return (nullptr);
211 : }
212 :
213 0 : nlohmann::json Dictionary::asJson(void) const {
214 0 : nlohmann::json result;
215 :
216 0 : result["name"] = name_;
217 :
218 : // Formats
219 0 : for (const auto &it : formats_) {
220 0 : if (it.second.isReserved()) continue;
221 0 : if (it.second.isRFC6733()) continue; // only user-defined formats are shown
222 0 : result["format"].push_back(it.second.asJson());
223 : }
224 :
225 : // Vendors
226 0 : for (const auto &it : vendors_) result["vendor"].push_back(it.second.asJson());
227 :
228 : // Avps
229 0 : for (const auto &it : avps_) {
230 0 : if (it.second.getFormat()->isAny()) continue; // Generic AVP not shown
231 :
232 0 : result["avp"].push_back(it.second.asJson());
233 : }
234 :
235 : // Commands
236 0 : for (const auto &it : commands_) result["command"].push_back(it.second.asJson());
237 :
238 0 : return result;
239 0 : }
240 :
241 0 : void Dictionary::extractFormats(const nlohmann::json &doc) {
242 0 : for (const auto &it : doc) {
243 0 : Format aux(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
244 :
245 : // Mandatory
246 0 : auto name_it = it.find("name");
247 0 : auto ptype_it = it.find("parent-type");
248 :
249 : // Assignments:
250 0 : aux.setName(*name_it);
251 0 : aux.setParentName(*ptype_it);
252 :
253 : // New entry:
254 0 : addFormat(aux);
255 0 : }
256 0 : }
257 :
258 146 : void Dictionary::extractVendors(const nlohmann::json &doc) {
259 1898 : for (const auto &it : doc) {
260 876 : Vendor aux; // set everything below (even empty, zeroed, etc.) to avoid reset() function
261 :
262 : // Mandatory
263 876 : auto name_it = it.find("name");
264 876 : auto code_it = it.find("code");
265 :
266 : // Assignments:
267 876 : aux.setCode(*code_it);
268 876 : aux.setName(*name_it);
269 :
270 : // New entry:
271 876 : addVendor(aux);
272 876 : }
273 146 : }
274 :
275 158 : void Dictionary::extractAvps(const nlohmann::json &doc) {
276 15804 : for (const auto &it : doc) {
277 15646 : Avp aux(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
278 :
279 : // Mandatory
280 15646 : auto name_it = it.find("name");
281 15646 : std::string name = *name_it;
282 15646 : auto code_it = it.find("code");
283 15646 : auto vendor_name_it = it.find("vendor-name");
284 15646 : core::S32 vendorCode = 0; /* IETF by default */
285 15646 : auto single_it = it.find("single");
286 :
287 : // Optionals
288 15646 : auto vbit_it = it.find("v-bit");
289 15646 : auto mbit_it = it.find("m-bit");
290 :
291 : // Vendor ?
292 15646 : if (vendor_name_it != it.end()) {
293 0 : std::string c_name = *vendor_name_it;
294 0 : auto v_it = vendor_names_.find(c_name);
295 :
296 0 : if (v_it == vendor_names_.end()) {
297 : std::string s_ex = ert::tracing::Logger::asString(
298 0 : "Vendor '%s', referenced at '%s' avp definition, not found at xml", c_name, name.c_str());
299 0 : throw std::runtime_error(s_ex);
300 0 : }
301 :
302 0 : aux.setVendorName(c_name);
303 0 : vendorCode = ((*v_it).second)->getCode();
304 0 : }
305 :
306 : // Assignments:
307 15646 : aux.setCode(*code_it);
308 15646 : aux.setVendorId(vendorCode);
309 15646 : aux.setName(*name_it);
310 15646 : aux.setVBit((vbit_it != it.end()) ? bool(*vbit_it) : false);
311 15646 : aux.setMBit((mbit_it != it.end()) ? bool(*mbit_it) : false);
312 :
313 : // Check vendor specific bit:
314 15646 : if (vendorCode && !aux.vBit()) {
315 : std::string s_ex = ert::tracing::Logger::asString(
316 : "Flag rules for vendor specific bit (mustnot) at '%s' avp definicion, are incompatible with non-zeroed "
317 : "vendor id %d",
318 0 : name.c_str(), vendorCode);
319 0 : throw std::runtime_error(s_ex);
320 0 : }
321 :
322 15646 : if (!vendorCode && aux.vBit()) {
323 : std::string s_ex = ert::tracing::Logger::asString(
324 : "Flag rules for vendor specific bit (must) at '%s' avp definicion, are incompatible with zeroed vendor "
325 : "id %d",
326 0 : name.c_str(), vendorCode);
327 0 : throw std::runtime_error(s_ex);
328 0 : }
329 :
330 15646 : if (single_it != it.end()) {
331 13018 : auto f_it = (*single_it).find("format"); // mandatory
332 13018 : std::string formatName = *f_it;
333 13018 : auto enum_it = (*single_it).find("enum");
334 13018 : auto label_it = (*single_it).find("label");
335 :
336 : // Assignments:
337 13018 : const Format *format = getFormat(formatName);
338 :
339 13018 : if (!format) {
340 : std::string s_ex = ert::tracing::Logger::asString(
341 : "Format '%s', referenced at '%s' avp definition, not found at dictionary (neither xml nor RFC6733 "
342 : "diameter format types)",
343 0 : formatName.c_str(), name.c_str());
344 0 : throw std::runtime_error(s_ex);
345 0 : }
346 :
347 13018 : aux.setFormatName(formatName);
348 :
349 13018 : if (enum_it != (*single_it).end()) {
350 3358 : std::string s_enum = *enum_it;
351 :
352 3358 : if (!format->isEnumerated()) {
353 : std::string s_ex =
354 : ert::tracing::Logger::asString("Enumerated literal '%s' is not allowed for '%s' avp format",
355 0 : s_enum.c_str(), formatName.c_str());
356 0 : throw std::runtime_error(s_ex);
357 0 : }
358 :
359 3358 : aux.setEnums(s_enum.c_str());
360 3358 : }
361 :
362 13018 : if (label_it != (*single_it).end()) {
363 24820 : for (const auto &l_it : *label_it) {
364 20732 : std::string data = *(l_it.find("data"));
365 20732 : std::string alias = *(l_it.find("alias"));
366 : // Assignment:
367 20732 : aux.addLabel(data, alias);
368 20732 : }
369 : }
370 13018 : } else { // grouped
371 : // Assignments:
372 5256 : aux.setFormatName(Format::Type::asText(Format::Type::Grouped));
373 : // Wait for avprule insertion, because we need complete avp reference pool (*)
374 : }
375 :
376 : // New entry:
377 15646 : addAvp(aux);
378 15646 : }
379 :
380 : // Now process grouped ones:
381 15804 : for (const auto &it : doc) {
382 15646 : auto name_it = it.find("name");
383 15646 : auto grouped_it = it.find("grouped");
384 :
385 15646 : auto a_it = avp_names_.find(*name_it);
386 15646 : Avp *gavp = (Avp *)((*a_it).second);
387 :
388 15646 : if (!gavp) continue; // it could be mising (a redefinition could have removed it)
389 :
390 15646 : const Format *format = gavp->getFormat();
391 :
392 : // Avprule updating:
393 15646 : if (format->isGrouped()) { // double check
394 2628 : auto avprule_it = grouped_it->find("avprule");
395 2628 : AvpRule auxAvpRule(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
396 12264 : for (const auto &it : *avprule_it) {
397 9636 : std::string name = *(it.find("name"));
398 9636 : std::string type = *(it.find("type"));
399 9636 : auto qual_it = it.find("qual"); // optional
400 :
401 9636 : const Avp *avp = getAvp(name);
402 9636 : if (avp == nullptr) {
403 : std::string s_ex = ert::tracing::Logger::asString(
404 : "Avp '%s', referenced at avp rule definition within grouped '%s', not found at xml",
405 0 : name.c_str(), std::string(*name_it).c_str());
406 0 : throw std::runtime_error(s_ex);
407 0 : }
408 :
409 9636 : auxAvpRule.setAvpId(avp->getId());
410 9636 : auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
411 9636 : auxAvpRule.setQual((qual_it != it.end()) ? *qual_it : "");
412 9636 : gavp->addAvpRule(auxAvpRule);
413 9636 : }
414 2628 : }
415 : }
416 :
417 : // Check avp loops between grouped avps:
418 :
419 : // In order to avoid loops, we could force to define grouped avps which children
420 : // had been previously defined at xml file. In this way, is imposible to get a loop:
421 : // C = ...
422 : // D = ...
423 : // A = grouped of B,C,D -> error, B unknown
424 : // B = grouped of A,F -> with former definition, would become a loop
425 : //
426 : // But this supposes a restriction at json configuration (specific order).
427 : // The other way is an internal check: a grouped AVP won't have descendants within
428 : // its ascendants. Then we will check all grouped avps in this way:
429 : //
430 : // 1. Searching for another grouped avps which are parents for this avp.
431 : // 2. If these are children (even this avp(*)) at avp definition, then a loop is detected.
432 : //
433 : // Example 1: (1) Analyzing 'A', found parent 'B' / (2) 'B' is already children of 'A'
434 : // A -> B
435 : // C
436 : // D
437 : // ...
438 : // B -> A -> loop !!
439 : // F
440 : //
441 : // (*) Example 2: (1) Analyzing 'A', found parent 'A' / (2) 'A' is already children of 'A'
442 : // A -> B
443 : // C
444 : // D
445 : // A -> loop !!
446 : //
447 17246 : for (auto it = avps_.begin(); it != avps_.end(); it++) {
448 17088 : const Avp &avp = (*it).second;
449 :
450 17088 : if (!((avp.getFormat())->isGrouped())) continue;
451 :
452 310428 : for (auto it_p = avps_.begin(); it_p != avps_.end(); it_p++) {
453 307584 : const Avp &avp_p = (*it_p).second;
454 :
455 307584 : if (!((avp_p.getFormat())->isGrouped())) continue;
456 :
457 51192 : if (avp_p.isChild(avp.getId())) {
458 1896 : if (avp.isChild(avp_p.getId())) {
459 0 : std::string s_ex;
460 :
461 0 : if (it != it_p)
462 0 : s_ex = ert::tracing::Logger::asString("Loop detected between grouped avps '%s' and '%s'",
463 0 : avp.getName().c_str(), avp_p.getName().c_str());
464 : else
465 0 : s_ex = ert::tracing::Logger::asString("Loop within grouped avp '%s': cannot contain itself !!",
466 0 : avp.getName().c_str());
467 :
468 0 : throw std::runtime_error(s_ex);
469 0 : } // parent is children of (ref): loop !
470 : } // parent found
471 : } // search parents
472 : } // search grouped avps (ref)
473 158 : }
474 :
475 146 : void Dictionary::extractCommands(const nlohmann::json &doc) {
476 4234 : for (const auto &it : doc) {
477 2044 : Command aux; // set everything below (even empty, zeroed, etc.) to avoid reset() function
478 :
479 : // Mandatory
480 2044 : auto name_it = it.find("name");
481 2044 : auto code_it = it.find("code");
482 2044 : auto avprule_it = it.find("avprule");
483 :
484 : // Optionals
485 2044 : auto appid_it = it.find("application-id");
486 2044 : auto rbit_it = it.find("r-bit");
487 2044 : auto pbit_it = it.find("p-bit");
488 :
489 : // Assignments:
490 2044 : aux.setName(*name_it);
491 2044 : aux.setCode(*code_it);
492 2044 : aux.setApplicationId((appid_it != it.end()) ? core::U32(*appid_it) : 0);
493 2044 : aux.setRequest((rbit_it != it.end()) ? bool(*rbit_it) : false);
494 2044 : aux.setPBit((pbit_it != it.end()) ? bool(*pbit_it) : false);
495 :
496 2044 : AvpRule auxAvpRule(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
497 49640 : for (const auto &it : *avprule_it) {
498 23798 : std::string name = *(it.find("name"));
499 23798 : std::string type = *(it.find("type"));
500 23798 : auto qual_it = it.find("qual"); // optional
501 :
502 23798 : const Avp *avp = getAvp(name);
503 23798 : if (avp == nullptr) {
504 : std::string s_ex = ert::tracing::Logger::asString(
505 : "Avp '%s', referenced at avp rule definition within command '%s', not found at xml", name.c_str(),
506 0 : std::string(*name_it).c_str());
507 0 : throw std::runtime_error(s_ex);
508 0 : }
509 :
510 23798 : auxAvpRule.setAvpId(avp->getId());
511 23798 : auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
512 23798 : auxAvpRule.setQual((qual_it != it.end()) ? *qual_it : "");
513 23798 : aux.addAvpRule(auxAvpRule);
514 23798 : }
515 :
516 : // New entry:
517 2044 : addCommand(aux);
518 2044 : }
519 146 : }
520 :
521 158 : void Dictionary::load(const nlohmann::json &json) {
522 : // Mandatory
523 158 : auto name_it = json.find("name");
524 158 : name_ = *name_it;
525 :
526 : // Optional
527 158 : auto formats_it = json.find("format");
528 158 : if (formats_it != json.end() && formats_it->is_array()) {
529 0 : extractFormats(*formats_it);
530 : }
531 :
532 158 : auto vendors_it = json.find("vendor");
533 158 : if (vendors_it != json.end() && vendors_it->is_array()) {
534 146 : extractVendors(*vendors_it);
535 : }
536 :
537 158 : auto avps_it = json.find("avp");
538 158 : if (avps_it != json.end() && avps_it->is_array()) {
539 158 : extractAvps(*avps_it);
540 : }
541 :
542 158 : auto commands_it = json.find("command");
543 158 : if (commands_it != json.end() && commands_it->is_array()) {
544 146 : extractCommands(*commands_it);
545 : }
546 158 : }
547 :
548 : } // namespace stack
549 : } // namespace diametercodec
550 : } // namespace ert
|