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 :
42 : // Project
43 : #include <ert/tracing/Logger.hpp>
44 : #include <ert/diametercodec/stack/Dictionary.hpp>
45 :
46 :
47 : namespace ert
48 : {
49 : namespace diametercodec
50 : {
51 : namespace stack
52 : {
53 :
54 25 : void Dictionary::initialize() {
55 25 : formats_.clear();
56 25 : vendors_.clear();
57 25 : avps_.clear();
58 25 : commands_.clear();
59 25 : vendor_names_.clear();
60 25 : avp_names_.clear();
61 25 : command_names_.clear();
62 :
63 : // RFC6733 Diameter Formats harcoding:
64 : // Basic diameter types
65 25 : Format OctetString(this), Integer32(this), Integer64(this), Unsigned32(this), Unsigned64(this), Float32(this), Float64(this), Grouped(this);
66 25 : OctetString.setType(Format::Type::OctetString);
67 25 : Integer32.setType(Format::Type::Integer32);
68 25 : Integer64.setType(Format::Type::Integer64);
69 25 : Unsigned32.setType(Format::Type::Unsigned32);
70 25 : Unsigned64.setType(Format::Type::Unsigned64);
71 25 : Float32.setType(Format::Type::Float32);
72 25 : Float64.setType(Format::Type::Float64);
73 25 : Grouped.setType(Format::Type::Grouped);
74 :
75 25 : addFormat(OctetString);
76 25 : addFormat(Integer32);
77 25 : addFormat(Integer64);
78 25 : addFormat(Unsigned32);
79 25 : addFormat(Unsigned64);
80 25 : addFormat(Float32);
81 25 : addFormat(Float64);
82 25 : addFormat(Grouped);
83 :
84 : // Derived diameter types
85 25 : Format Address(this), Time(this), UTF8String(this), DiameterIdentity(this), DiameterURI(this), Enumerated(this), IPFilterRule(this), QoSFilterRule(this);
86 25 : Address.setType(Format::Type::Address);
87 25 : Address.setParentName(OctetString.getName());
88 25 : Time.setType(Format::Type::Time);
89 25 : Time.setParentName(OctetString.getName());
90 25 : UTF8String.setType(Format::Type::UTF8String);
91 25 : UTF8String.setParentName(OctetString.getName());
92 25 : DiameterIdentity.setType(Format::Type::DiameterIdentity);
93 25 : DiameterIdentity.setParentName(OctetString.getName());
94 25 : DiameterURI.setType(Format::Type::DiameterURI);
95 25 : DiameterURI.setParentName(OctetString.getName());
96 25 : Enumerated.setType(Format::Type::Enumerated);
97 25 : Enumerated.setParentName(Integer32.getName());
98 25 : IPFilterRule.setType(Format::Type::IPFilterRule);
99 25 : IPFilterRule.setParentName(OctetString.getName());
100 25 : QoSFilterRule.setType(Format::Type::QoSFilterRule);
101 25 : QoSFilterRule.setParentName(OctetString.getName());
102 :
103 25 : addFormat(Address);
104 25 : addFormat(Time);
105 25 : addFormat(UTF8String);
106 25 : addFormat(DiameterIdentity);
107 25 : addFormat(DiameterURI);
108 25 : addFormat(Enumerated);
109 25 : addFormat(IPFilterRule);
110 25 : addFormat(QoSFilterRule);
111 :
112 : // Generic AVP format:
113 25 : Format Any(this);
114 25 : Any.setType(Format::Type::Any);
115 25 : addFormat(Any, true /*reserved*/);
116 :
117 : // Generic AVP:
118 25 : Avp genericAvp(this);
119 25 : genericAvp.setCode(0);
120 25 : genericAvp.setVendorId(0/*Vendor::Code::Ietf*/);
121 25 : genericAvp.setName("AVP");
122 25 : genericAvp.setFormatName(Any.getName());
123 25 : genericAvp.setVBit(false);
124 25 : genericAvp.setMBit(false);
125 25 : addAvp(genericAvp);
126 25 : }
127 :
128 425 : void Dictionary::addFormat(const Format & format, bool reserved) {
129 425 : if(!reserved && format.isReserved()) {
130 0 : std::string s_ex = ert::tracing::Logger::asString("Format type '%s' is reserved for internal use", format.getName().c_str());
131 0 : throw std::runtime_error(s_ex);
132 0 : }
133 :
134 425 : const Format * found = getFormat(format.getName());
135 425 : if(found) {
136 : // Update:
137 0 : LOGINFORMATIONAL(
138 : std::string trace = "Updated format '";
139 : trace += format.getName();
140 : trace += "'";
141 : ert::tracing::Logger::informational(trace, ERT_FILE_LOCATION);
142 : );
143 : }
144 :
145 425 : formats_[format.getName()] = format;
146 425 : }
147 :
148 150 : void Dictionary::addVendor(const Vendor & vendor) {
149 :
150 150 : vendors_[vendor.getCode()] = vendor;
151 150 : vendor_names_[vendor.getName()] = getVendor(vendor.getCode());
152 150 : }
153 :
154 2700 : void Dictionary::addAvp(const Avp & avp) {
155 :
156 2700 : avps_[avp.getId()] = avp;
157 2700 : avp_names_[avp.getName()] = getAvp(avp.getId());
158 2700 : }
159 :
160 350 : void Dictionary::addCommand(const Command & command) {
161 :
162 350 : commands_[command.getId()] = command;
163 350 : command_names_[command.getName()] = getCommand(command.getId());
164 350 : }
165 :
166 72159 : const Format * Dictionary::getFormat(const std::string & formatName) const {
167 72159 : auto it = formats_.find(formatName);
168 :
169 72159 : if(it != formats_.end()) return &(it->second);
170 :
171 425 : return (nullptr);
172 : }
173 :
174 150 : const Vendor * Dictionary::getVendor(core::S32 vendorId) const {
175 150 : auto it = vendors_.find(vendorId);
176 :
177 150 : if(it != vendors_.end()) return &(it->second);
178 :
179 0 : return (nullptr);
180 : }
181 :
182 0 : const Vendor * Dictionary::getVendor(const std::string & vendorName) const {
183 0 : auto it = vendor_names_.find(vendorName);
184 :
185 0 : if(it != vendor_names_.end()) return (it->second);
186 :
187 0 : return (nullptr);
188 : }
189 :
190 2705 : Avp * Dictionary::getAvp(const core::AvpId & avpId) const {
191 2705 : auto it = avps_.find(avpId);
192 :
193 2705 : if(it != avps_.end()) return (Avp*)&(it->second);
194 :
195 0 : return (nullptr);
196 : }
197 :
198 5726 : Avp * Dictionary::getAvp(const std::string & avpName) const {
199 5726 : auto it = avp_names_.find(avpName);
200 :
201 5726 : if(it != avp_names_.end()) return (Avp*)(it->second);
202 :
203 0 : return (nullptr);
204 : }
205 :
206 350 : Command * Dictionary::getCommand(const core::CommandId & commandId) const {
207 350 : auto it = commands_.find(commandId);
208 :
209 350 : if(it != commands_.end()) return (Command*)&(it->second);
210 :
211 0 : return (nullptr);
212 : }
213 :
214 0 : Command * Dictionary::getCommand(const std::string & commandName) const {
215 0 : auto it = command_names_.find(commandName);
216 :
217 0 : if(it != command_names_.end()) return (Command*)(it->second);
218 :
219 0 : return (nullptr);
220 : }
221 :
222 0 : nlohmann::json Dictionary::asJson(void) const {
223 0 : nlohmann::json result;
224 :
225 0 : result["name"] = name_;
226 :
227 : // Formats
228 0 : for(auto it: formats_) {
229 0 : if(it.second.isReserved()) continue;
230 0 : if(it.second.isRFC6733()) continue; // only user-defined formats are shown
231 0 : result["format"].push_back(it.second.asJson());
232 0 : }
233 :
234 : // Vendors
235 0 : for(auto it: vendors_)
236 0 : result["vendor"].push_back(it.second.asJson());
237 :
238 : // Avps
239 0 : for(auto it: avps_) {
240 0 : if(it.second.getFormat()->isAny()) continue; // Generic AVP not shown
241 :
242 0 : result["avp"].push_back(it.second.asJson());
243 0 : }
244 :
245 : // Commands
246 0 : for(auto it: commands_)
247 0 : result["command"].push_back(it.second.asJson());
248 :
249 0 : return result;
250 0 : }
251 :
252 0 : void Dictionary::extractFormats(const nlohmann::json &doc) {
253 0 : for(auto it: doc) {
254 0 : Format aux(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
255 :
256 : // Mandatory
257 0 : auto name_it = it.find("name");
258 0 : auto ptype_it = it.find("parent-type");
259 :
260 : // Assignments:
261 0 : aux.setName(*name_it);
262 0 : aux.setParentName(*ptype_it);
263 :
264 : // New entry:
265 0 : addFormat(aux);
266 0 : }
267 0 : }
268 :
269 25 : void Dictionary::extractVendors(const nlohmann::json &doc) {
270 325 : for(auto it: doc) {
271 150 : Vendor aux; // set everything below (even empty, zeroed, etc.) to avoid reset() function
272 :
273 : // Mandatory
274 150 : auto name_it = it.find("name");
275 150 : auto code_it = it.find("code");
276 :
277 : // Assignments:
278 150 : aux.setCode(*code_it);
279 150 : aux.setName(*name_it);
280 :
281 : // New entry:
282 150 : addVendor(aux);
283 150 : }
284 25 : }
285 :
286 25 : void Dictionary::extractAvps(const nlohmann::json &doc) {
287 2700 : for(auto it: doc) {
288 2675 : Avp aux(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
289 :
290 : // Mandatory
291 2675 : auto name_it = it.find("name");
292 2675 : std::string name = *name_it;
293 2675 : auto code_it = it.find("code");
294 2675 : auto vendor_name_it = it.find("vendor-name");
295 2675 : core::S32 vendorCode = 0; /* IETF by default */
296 2675 : auto single_it = it.find("single");
297 :
298 : // Optionals
299 2675 : auto vbit_it = it.find("v-bit");
300 2675 : auto mbit_it = it.find("m-bit");
301 :
302 : // Vendor ?
303 2675 : if (vendor_name_it != it.end()) {
304 0 : std::string c_name = *vendor_name_it;
305 0 : auto v_it = vendor_names_.find(c_name);
306 :
307 0 : if(v_it == vendor_names_.end()) {
308 0 : std::string s_ex = ert::tracing::Logger::asString("Vendor '%s', referenced at '%s' avp definition, not found at xml", c_name, name.c_str());
309 0 : throw std::runtime_error(s_ex);
310 0 : }
311 :
312 0 : aux.setVendorName(c_name);
313 0 : vendorCode = ((*v_it).second)->getCode();
314 0 : }
315 :
316 : // Assignments:
317 2675 : aux.setCode(*code_it);
318 2675 : aux.setVendorId(vendorCode);
319 2675 : aux.setName(*name_it);
320 2675 : aux.setVBit((vbit_it!=it.end()) ? bool(*vbit_it) : false);
321 2675 : aux.setMBit((mbit_it!=it.end()) ? bool(*mbit_it) : false);
322 :
323 : // Check vendor specific bit:
324 2675 : if(vendorCode && !aux.vBit()) {
325 0 : std::string s_ex = ert::tracing::Logger::asString("Flag rules for vendor specific bit (mustnot) at '%s' avp definicion, are incompatible with non-zeroed vendor id %d", name.c_str(), vendorCode);
326 0 : throw std::runtime_error(s_ex);
327 0 : }
328 :
329 2675 : if(!vendorCode && aux.vBit()) {
330 0 : std::string s_ex = ert::tracing::Logger::asString("Flag rules for vendor specific bit (must) at '%s' avp definicion, are incompatible with zeroed vendor id %d", name.c_str(), vendorCode);
331 0 : throw std::runtime_error(s_ex);
332 0 : }
333 :
334 2675 : if(single_it != it.end()) {
335 2225 : auto f_it = (*single_it).find("format"); // mandatory
336 2225 : std::string formatName = *f_it;
337 2225 : auto enum_it = (*single_it).find("enum");
338 2225 : auto label_it = (*single_it).find("label");
339 :
340 : // Assignments:
341 2225 : const Format *format = getFormat(formatName);
342 :
343 2225 : if(!format) {
344 0 : std::string s_ex = ert::tracing::Logger::asString("Format '%s', referenced at '%s' avp definition, not found at dictionary (neither xml nor RFC6733 diameter format types)", formatName.c_str(), name.c_str());
345 0 : throw std::runtime_error(s_ex);
346 0 : }
347 :
348 2225 : aux.setFormatName(formatName);
349 :
350 2225 : if(enum_it != (*single_it).end()) {
351 :
352 575 : std::string s_enum = *enum_it;
353 :
354 575 : if(!format->isEnumerated()) {
355 0 : std::string s_ex = ert::tracing::Logger::asString("Enumerated literal '%s' is not allowed for '%s' avp format", s_enum.c_str(), formatName.c_str());
356 0 : throw std::runtime_error(s_ex);
357 0 : }
358 :
359 575 : aux.setEnums(s_enum.c_str());
360 575 : }
361 :
362 2225 : if(label_it != (*single_it).end()) {
363 4250 : for(auto l_it: *label_it) {
364 3550 : std::string data = *(l_it.find("data"));
365 3550 : std::string alias = *(l_it.find("alias"));
366 : // Assignment:
367 3550 : aux.addLabel(data, alias);
368 3550 : }
369 : }
370 2225 : } else { // grouped
371 : // Assignments:
372 900 : 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 2675 : addAvp(aux);
378 2675 : }
379 :
380 : // Now process grouped ones:
381 2700 : for(auto it: doc) {
382 2675 : auto name_it = it.find("name");
383 2675 : auto grouped_it = it.find("grouped");
384 :
385 2675 : auto a_it = avp_names_.find(*name_it);
386 2675 : Avp * gavp = (Avp *)((*a_it).second);
387 :
388 2675 : if(!gavp) continue; // it could be mising (a redefinition could have removed it)
389 :
390 2675 : const Format *format = gavp->getFormat();
391 :
392 : // Avprule updating:
393 2675 : if(format->isGrouped()) { // double check
394 450 : auto avprule_it = grouped_it->find("avprule");
395 450 : AvpRule auxAvpRule(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
396 2100 : for(auto it: *avprule_it) {
397 1650 : std::string name = *(it.find("name"));
398 1650 : std::string type = *(it.find("type"));
399 1650 : auto qual_it = it.find("qual"); // optional
400 :
401 1650 : const Avp * avp = getAvp(name);
402 1650 : if(avp == nullptr) {
403 0 : std::string s_ex = ert::tracing::Logger::asString("Avp '%s', referenced at avp rule definition within grouped '%s', not found at xml", name.c_str(), std::string(*name_it).c_str());
404 0 : throw std::runtime_error(s_ex);
405 0 : }
406 :
407 1650 : auxAvpRule.setAvpId(avp->getId());
408 1650 : auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
409 1650 : auxAvpRule.setQual((qual_it != it.end()) ? *qual_it:"");
410 1650 : gavp->addAvpRule(auxAvpRule);
411 1650 : }
412 450 : }
413 2675 : }
414 :
415 : // Check avp loops between grouped avps:
416 :
417 : // In order to avoid loops, we could force to define grouped avps which children
418 : // had been previously defined at xml file. In this way, is imposible to get a loop:
419 : // C = ...
420 : // D = ...
421 : // A = grouped of B,C,D -> error, B unknown
422 : // B = grouped of A,F -> with former definition, would become a loop
423 : //
424 : // But this supposes a restriction at json configuration (specific order).
425 : // The other way is an internal check: a grouped AVP won't have descendants within
426 : // its ascendants. Then we will check all grouped avps in this way:
427 : //
428 : // 1. Searching for another grouped avps which are parents for this avp.
429 : // 2. If these are children (even this avp(*)) at avp definition, then a loop is detected.
430 : //
431 : // Example 1: (1) Analyzing 'A', found parent 'B' / (2) 'B' is already children of 'A'
432 : // A -> B
433 : // C
434 : // D
435 : // ...
436 : // B -> A -> loop !!
437 : // F
438 : //
439 : // (*) Example 2: (1) Analyzing 'A', found parent 'A' / (2) 'A' is already children of 'A'
440 : // A -> B
441 : // C
442 : // D
443 : // A -> loop !!
444 : //
445 2725 : for(auto it = avps_.begin(); it != avps_.end(); it++) {
446 2700 : const Avp & avp = (*it).second;
447 :
448 2700 : if(!((avp.getFormat())->isGrouped())) continue;
449 :
450 49050 : for(auto it_p = avps_.begin(); it_p != avps_.end(); it_p++) {
451 48600 : const Avp & avp_p = (*it_p).second;
452 :
453 48600 : if(!((avp_p.getFormat())->isGrouped())) continue;
454 :
455 8100 : if(avp_p.isChild(avp.getId())) {
456 300 : if(avp.isChild(avp_p.getId())) {
457 0 : std::string s_ex;
458 :
459 :
460 0 : if(it != it_p)
461 0 : s_ex = ert::tracing::Logger::asString("Loop detected between grouped avps '%s' and '%s'", avp.getName().c_str(), avp_p.getName().c_str());
462 : else
463 0 : s_ex = ert::tracing::Logger::asString("Loop within grouped avp '%s': cannot contain itself !!", avp.getName().c_str());
464 :
465 0 : throw std::runtime_error(s_ex);
466 0 : } // parent is children of (ref): loop !
467 : } // parent found
468 : } // search parents
469 : } // search grouped avps (ref)
470 25 : }
471 :
472 25 : void Dictionary::extractCommands(const nlohmann::json &doc) {
473 725 : for(auto it: doc) {
474 350 : Command aux; // set everything below (even empty, zeroed, etc.) to avoid reset() function
475 :
476 : // Mandatory
477 350 : auto name_it = it.find("name");
478 350 : auto code_it = it.find("code");
479 350 : auto avprule_it = it.find("avprule");
480 :
481 : // Optionals
482 350 : auto appid_it = it.find("application-id");
483 350 : auto rbit_it = it.find("r-bit");
484 350 : auto pbit_it = it.find("p-bit");
485 :
486 : // Assignments:
487 350 : aux.setName(*name_it);
488 350 : aux.setCode(*code_it);
489 350 : aux.setApplicationId((appid_it!=it.end()) ? core::U32(*appid_it) : 0);
490 350 : aux.setRequest((rbit_it!=it.end()) ? bool(*rbit_it) : false);
491 350 : aux.setPBit((pbit_it!=it.end()) ? bool(*pbit_it) : false);
492 :
493 350 : AvpRule auxAvpRule(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
494 8500 : for(auto it: *avprule_it) {
495 4075 : std::string name = *(it.find("name"));
496 4075 : std::string type = *(it.find("type"));
497 4075 : auto qual_it = it.find("qual"); // optional
498 :
499 4075 : const Avp * avp = getAvp(name);
500 4075 : if(avp == nullptr) {
501 0 : std::string s_ex = ert::tracing::Logger::asString("Avp '%s', referenced at avp rule definition within command '%s', not found at xml", name.c_str(), std::string(*name_it).c_str());
502 0 : throw std::runtime_error(s_ex);
503 0 : }
504 :
505 4075 : auxAvpRule.setAvpId(avp->getId());
506 4075 : auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
507 4075 : auxAvpRule.setQual((qual_it != it.end()) ? *qual_it:"");
508 4075 : aux.addAvpRule(auxAvpRule);
509 4075 : }
510 :
511 : // New entry:
512 350 : addCommand(aux);
513 350 : }
514 25 : }
515 :
516 25 : void Dictionary::load(const nlohmann::json &json) {
517 :
518 : // Mandatory
519 25 : auto name_it = json.find("name");
520 25 : name_ = *name_it;
521 :
522 : // Optional
523 25 : auto formats_it = json.find("format");
524 25 : if (formats_it != json.end() && formats_it->is_array()) {
525 0 : extractFormats(*formats_it);
526 : }
527 :
528 25 : auto vendors_it = json.find("vendor");
529 25 : if (vendors_it != json.end() && vendors_it->is_array()) {
530 25 : extractVendors(*vendors_it);
531 : }
532 :
533 25 : auto avps_it = json.find("avp");
534 25 : if (avps_it != json.end() && avps_it->is_array()) {
535 25 : extractAvps(*avps_it);
536 : }
537 :
538 25 : auto commands_it = json.find("command");
539 25 : if (commands_it != json.end() && commands_it->is_array()) {
540 25 : extractCommands(*commands_it);
541 : }
542 25 : }
543 :
544 : }
545 : }
546 : }
547 :
|