1 /**********************************************************************************************************
2  * Software License Agreement (BSD License)                                                               *
3  * Author: Thomas Klausner <tk@giga.or.at>                                                                *
4  *                                                                                                        *
5  * Copyright (c) 2016, 2017, 2019 Thomas Klausner                                                         *
6  * All rights reserved.                                                                                   *
7  *                                                                                                        *
8  * Written under contract by nfotex IT GmbH, http://nfotex.com/                                           *
9  *                                                                                                        *
10  * Redistribution and use of this software in source and binary forms, with or without modification, are  *
11  * permitted provided that the following conditions are met:                                              *
12  *                                                                                                        *
13  * * Redistributions of source code must retain the above                                                 *
14  *   copyright notice, this list of conditions and the                                                    *
15  *   following disclaimer.                                                                                *
16  *                                                                                                        *
17  * * Redistributions in binary form must reproduce the above                                              *
18  *   copyright notice, this list of conditions and the                                                    *
19  *   following disclaimer in the documentation and/or other                                               *
20  *   materials provided with the distribution.                                                            *
21  *                                                                                                        *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
23  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
24  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
25  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
28  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
30  **********************************************************************************************************/
31 
32 /*
33  * Extend a dictionary using data from a JSON file.
34  *
35  */
36 #include <freeDiameter/extension.h>
37 #include <json/json.h>
38 #include <json/SchemaValidator.h>
39 #include <sys/stat.h>
40 
41 extern const char *dict_json_dict_schema;
42 
43 static int
make_type(const char * typestring,struct dict_object ** type)44 make_type(const char *typestring, struct dict_object **type)
45 {
46         *type = NULL;
47 
48         if (strcmp("Unsigned32", typestring) == 0)
49                 return AVP_TYPE_UNSIGNED32;
50         else if (strcmp("Enumerated", typestring) == 0 || strcmp("Integer32", typestring) == 0)
51                 return AVP_TYPE_INTEGER32;
52         else if (strcmp("OctetString", typestring) == 0)
53                 return AVP_TYPE_OCTETSTRING;
54         else if (strcmp("Grouped", typestring) == 0)
55                 return AVP_TYPE_GROUPED;
56         else if (strcmp("Integer64", typestring) == 0)
57                 return AVP_TYPE_INTEGER64;
58         else if (strcmp("Unsigned64", typestring) == 0)
59                 return AVP_TYPE_UNSIGNED64;
60         else if (strcmp("Float32", typestring) == 0)
61                 return AVP_TYPE_FLOAT32;
62         else if (strcmp("Float64", typestring) == 0)
63                 return AVP_TYPE_FLOAT64;
64         else {
65                 if (fd_dict_search(fd_g_config->cnf_dict, DICT_TYPE, TYPE_BY_NAME, typestring, type, ENOENT) != 0) {
66                         LOG_E("Unknown type '%s'", typestring);
67                         return -1;
68                 }
69                 return AVP_TYPE_OCTETSTRING;
70         }
71 
72         return -1;
73 }
74 
75 
76 static uint8_t
make_avp_flags(const char * flagstring)77 make_avp_flags(const char *flagstring)
78 {
79         uint8_t flags = 0;
80 
81         if (strchr(flagstring, 'M') != NULL)
82                 flags |= AVP_FLAG_MANDATORY;
83         if (strchr(flagstring, 'V') != NULL)
84                 flags |= AVP_FLAG_VENDOR;
85 
86         return flags;
87 }
88 
89 static uint8_t
make_command_flags(const char * flagstring)90 make_command_flags(const char *flagstring)
91 {
92         uint8_t flags = 0;
93 
94         if (strchr(flagstring, 'E') != NULL)
95                 flags |= CMD_FLAG_ERROR;
96         if (strchr(flagstring, 'P') != NULL)
97                 flags |= CMD_FLAG_PROXIABLE;
98         if (strchr(flagstring, 'R') != NULL)
99                 flags |= CMD_FLAG_REQUEST;
100 
101         return flags;
102 }
103 
104 
105 static bool
add_applications(const Json::Value & config)106 add_applications(const Json::Value &config)
107 {
108         Json::Value applications = config["Applications"];
109         if (applications == Json::Value::null)
110                 return true;
111         for (Json::ArrayIndex i=0; i<applications.size(); i++) {
112                 int ret;
113                 struct dict_application_data application_data;
114                 application_data.application_id = applications[i]["Code"].asUInt();
115                 application_data.application_name = (char *)(void *)applications[i]["Name"].asCString();
116                 if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, &application_data, NULL, NULL)) != 0) {
117                         LOG_E("error adding Application '%s' to dictionary: %s", applications[i]["Name"].asCString(), strerror(ret));
118                         return false;
119                 }
120                 LOG_D("Added Application '%s' to dictionary", applications[i]["Name"].asCString());
121         }
122 
123         return true;
124 }
125 
126 static bool
add_enumtype(const char * enumtypename,enum dict_avp_basetype basetype,struct dict_object ** enumtype)127 add_enumtype(const char *enumtypename, enum dict_avp_basetype basetype, struct dict_object **enumtype)
128 {
129         struct dict_type_data data;
130         int ret;
131 
132         memset(&data, 0, sizeof(data));
133         data.type_base = basetype;
134         data.type_name = (char *)(void *)enumtypename;
135         data.type_interpret = NULL;
136         data.type_encode = NULL;
137         data.type_dump = NULL;
138         if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_TYPE, &data, NULL, enumtype)) != 0) {
139                 /* TODO: allow ret == EEXIST? */
140                 LOG_E("error defining type '%s': %s", enumtypename, strerror(ret));
141                 return false;
142         }
143         LOG_D("Added enumerated type '%s' to dictionary", enumtypename);
144 
145         return true;
146 }
147 
148 static unsigned int
hexchar(char input)149 hexchar(char input) {
150         if (input >= '0' and input <= '9')
151                 return input - '0';
152         if (input >= 'a' and input <= 'f')
153                 return input - 'a' + 10;
154         if (input >= 'A' and input <= 'F')
155                 return input - 'a' + 10;
156 
157         return 0;
158 }
159 
160 static char
hexdecode(const char * input)161 hexdecode(const char *input) {
162         return (char)(hexchar(input[0])*16 + hexchar(input[1]));
163 }
164 
165 static char *
unpack_enumval_os(const Json::Value & enumvalue,unsigned int & len)166 unpack_enumval_os(const Json::Value &enumvalue, unsigned int &len)
167 {
168         if (!enumvalue.isString()) {
169                 len = 0;
170                 return NULL;
171         }
172         len = enumvalue.asString().size();
173         if (enumvalue.asString()[0] == '<' && enumvalue.asString()[len-1] == '>') {
174                 char *retstr;
175                 len = len/2 - 1;
176                 if ((retstr = (char *)malloc(len)) == NULL) {
177                         return retstr;
178                 }
179                 for (size_t i=0; i<len; i++) {
180                         retstr[i] = hexdecode(enumvalue.asCString()+2*i+1);
181                 }
182                 return retstr;
183         }
184 
185         return strdup(enumvalue.asCString());
186 }
187 
188 static bool
add_enumvalue(const Json::Value & enumvalue,enum dict_avp_basetype basetype,struct dict_object * enumtype,const char * avpname)189 add_enumvalue(const Json::Value &enumvalue, enum dict_avp_basetype basetype, struct dict_object *enumtype, const char *avpname)
190 {
191         struct dict_enumval_data data;
192         int ret;
193         bool free_os = false;
194 
195         memset(&data, 0, sizeof(data));
196         data.enum_name = (char *)(void *)enumvalue["Name"].asCString();
197         switch (basetype) {
198         case AVP_TYPE_INTEGER32:
199                 if (enumvalue["Code"].isInt()) {
200                         data.enum_value.i32 = enumvalue["Code"].asInt();
201                 } else {
202                         LOG_E("unsupported EnumValue '%s' for AVP '%s', base type Integer32", enumvalue["Name"].asCString(), avpname);
203                         return false;
204                 }
205                 break;
206         case AVP_TYPE_UNSIGNED32:
207                 if (enumvalue["Code"].isInt()) {
208                         data.enum_value.u32 = enumvalue["Code"].asInt();
209                 } else {
210                         LOG_E("unsupported EnumValue '%s' for AVP '%s', base type Unsigned32", enumvalue["Name"].asCString(), avpname);
211                         return false;
212                 }
213                 break;
214         case AVP_TYPE_OCTETSTRING:
215                 if (enumvalue["Code"].isString()) {
216                         unsigned int len;
217                         data.enum_value.os.data = (unsigned char *)unpack_enumval_os(enumvalue["Code"], len);
218                         data.enum_value.os.len = len;
219                         free_os = true;
220                 } else {
221                         LOG_E("unsupported EnumValue '%s' for AVP '%s', base type OctetString", enumvalue["Name"].asCString(), avpname);
222                         return false;
223                 }
224                 break;
225         /* TODO: add other cases when we find that they occur */
226         default:
227                 LOG_E("unsupported EnumValue type '%s' for AVP '%s', type %d", enumvalue["Name"].asCString(), avpname, (int)basetype);
228                 return false;
229         }
230 
231         if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_ENUMVAL, &data, enumtype, NULL)) != 0) {
232                 LOG_E("error adding EnumValue '%s' for AVP '%s' to dictionary: %s", enumvalue["Name"].asCString(), avpname, strerror(errno));
233                 return false;
234         }
235         LOG_D("Added enumerated value '%s' to dictionary", enumvalue["Name"].asCString());
236 
237         if (free_os) {
238                 free(data.enum_value.os.data);
239         }
240 
241         return true;
242 }
243 
244 static bool
add_avp(const Json::Value & avp)245 add_avp(const Json::Value &avp)
246 {
247         int bt;
248         struct dict_avp_data data;
249         struct dict_object *type = NULL;
250         struct dict_avp_request dar;
251         struct dict_object *existing_avp_obj;
252         int fds_ret;
253 
254         if (!avp["Code"] || !avp["Name"] || !avp["Type"]) {
255                 LOG_E("invalid AVP, need Code, Name, and Type");
256                 return false;
257         }
258 
259         memset(&data, 0, sizeof(data));
260 
261         data.avp_code = avp["Code"].asUInt();
262         data.avp_vendor = 0;
263         data.avp_name = (char *)(void *)avp["Name"].asCString();
264         data.avp_flag_mask = 0;
265         if (avp["Flags"]["Must"] != Json::Value::null) {
266                 data.avp_flag_mask = data.avp_flag_val = make_avp_flags(avp["Flags"]["Must"].asCString());
267         }
268         if (avp["Flags"]["MustNot"] != Json::Value::null) {
269                 data.avp_flag_mask |= make_avp_flags(avp["Flags"]["MustNot"].asCString());
270         }
271         if (avp["Vendor"] != Json::Value::null) {
272                 data.avp_vendor = avp["Vendor"].asUInt();
273                 if ((data.avp_flag_mask & AVP_FLAG_VENDOR) == 0) {
274                         LOG_D("Vendor flag not set for AVP '%s'", data.avp_name);
275                         // error out?
276                 }
277                 data.avp_flag_mask |= AVP_FLAG_VENDOR;
278                 data.avp_flag_val |= AVP_FLAG_VENDOR;
279         }
280         bt = make_type(avp["Type"].asCString(), &type);
281         if (bt == -1) {
282                 LOG_E("Unknown AVP type %s", avp["Type"].asCString());
283                 return false;
284         }
285         data.avp_basetype = (enum dict_avp_basetype)bt;
286 
287         if (avp["EnumValues"] != Json::Value::null || avp["Type"] == "Enumerated") {
288                 if (avp["Type"] != "Enumerated") {
289                         LOG_D("AVP '%s': EnumValues defined for non-Enumerated type (freeDiameter extension); type is '%s'", avp["Name"].asCString(), avp["Type"].asCString());
290                 }
291                 Json::Value enumvalues = avp["EnumValues"];
292                 std::string enumtypename = "Enumerated(" + avp["Name"].asString() + ")";
293                 if (!add_enumtype(enumtypename.c_str(), data.avp_basetype, &type)) {
294                         LOG_E("Unknown AVP Enum: %s", enumtypename.c_str());
295                         return false;
296                 }
297                 for (Json::ArrayIndex i=0; i<enumvalues.size(); i++) {
298                         if (!add_enumvalue(enumvalues[i], data.avp_basetype, type, avp["Name"].asCString())) {
299                                 LOG_E("Unknown AVP Enum Value: %s", avp["Name"].asCString());
300                                 return false;
301                         }
302                 }
303         }
304 
305         dar.avp_vendor = data.avp_vendor;
306         // dar.avp_code = data.avp_code;
307         dar.avp_name = data.avp_name;
308 
309         fds_ret = fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &dar, &existing_avp_obj, ENOENT);
310         if (fds_ret == 0) {
311                 struct dict_avp_data existing_avp;
312                 if (fd_dict_getval(existing_avp_obj, &existing_avp) < 0) {
313                         LOG_E("Cannot get information for AVP '%s' from dictionary", data.avp_name);
314                         return false;
315                 }
316                 if (data.avp_code != existing_avp.avp_code
317 //                    || data.avp_vendor != existing_avp.avp_vendor
318 //                    || strcmp(data.avp_name, existing_avp.avp_name) != 0
319 //                    || data.avp_flag_mask != existing_avp.avp_flag_mask
320                     || data.avp_vendor != existing_avp.avp_vendor
321                     || data.avp_basetype != existing_avp.avp_basetype) {
322                         LOG_E("AVP with name '%s' but different properties already exists in dictionary (existing vs. new: Code: %d/%d, Flags: %x/%x, Vendor Id: %d/%d, Basetype: %d/%d)", data.avp_name, data.avp_code, existing_avp.avp_code, data.avp_flag_mask, existing_avp.avp_flag_mask, data.avp_vendor, existing_avp.avp_vendor, data.avp_basetype, existing_avp.avp_basetype);
323                         return false;
324                 }
325         } else if (fds_ret == ENOENT) {
326                 switch(fd_dict_new(fd_g_config->cnf_dict, DICT_AVP, &data, type, NULL)) {
327                 case 0:
328                         LOG_D("Added AVP '%s' to dictionary", data.avp_name);
329                         break;
330                 case EINVAL:
331                         LOG_E("error adding AVP '%s' to dictionary: invalid data", data.avp_name);
332                         return false;
333                 case EEXIST:
334                         LOG_E("error adding AVP '%s' to dictionary: duplicate entry", data.avp_name);
335                         return false;
336                 default:
337                         LOG_E("error adding AVP '%s' to dictionary: %s", data.avp_name, strerror(errno));
338                         return false;
339                 }
340         } else {
341                 LOG_E("error looking up AVP '%s' in dictionary: %s", data.avp_name, strerror(errno));
342                 return false;
343         }
344 
345         return true;
346 }
347 
348 static bool
add_avps(const Json::Value & config)349 add_avps(const Json::Value &config)
350 {
351         Json::Value avps = config["AVPs"];
352         if (avps == Json::Value::null)
353                 return true;
354         for (Json::ArrayIndex i=0; i<avps.size(); i++) {
355                 if (!add_avp(avps[i])) {
356                         LOG_E("error adding AVP to dictionary");
357                         return false;
358                 }
359         }
360 
361         return true;
362 }
363 
364 static bool
look_up_application(const Json::Value & application,struct dict_object ** application_object)365 look_up_application(const Json::Value &application, struct dict_object **application_object)
366 {
367         fd_dict_search(fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, (void *)application.asCString(), application_object, 0);
368         if (*application_object == NULL) {
369                 LOG_E("Application '%s' not found", application.asCString());
370                 return false;
371         }
372         return true;
373 }
374 
375 static bool
add_commands(const Json::Value & config)376 add_commands(const Json::Value &config)
377 {
378         Json::Value commands = config["Commands"];
379         if (commands == Json::Value::null)
380                 return true;
381         for (Json::ArrayIndex i=0; i<commands.size(); i++) {
382                 int ret;
383                 struct dict_cmd_data command_data;
384                 command_data.cmd_code = commands[i]["Code"].asUInt();
385                 command_data.cmd_name = (char *)(void *)commands[i]["Name"].asCString();
386                 command_data.cmd_flag_mask = 0;
387                 command_data.cmd_flag_val = 0;
388                 if (commands[i]["Flags"]["Must"] != Json::Value::null) {
389                         command_data.cmd_flag_mask = command_data.cmd_flag_val = make_command_flags(commands[i]["Flags"]["Must"].asCString());
390                 }
391                 if (commands[i]["Flags"]["MustNot"] != Json::Value::null) {
392                         command_data.cmd_flag_mask |= make_command_flags(commands[i]["Flags"]["MustNot"].asCString());
393                 }
394                 if (commands[i]["Application"] != Json::Value::null) {
395                         struct dict_object *application_object;
396                         if (!look_up_application(commands[i]["Application"], &application_object)) {
397                                 LOG_E("Application not found in dictionary");
398                                 return false;
399                         }
400                         if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_COMMAND, &command_data, application_object, NULL)) != 0) {
401                                 LOG_E("error adding Command '%s' to dictionary: %s", commands[i]["Name"].asCString(), strerror(ret));
402                                 return false;
403                         }
404                 } else {
405                         if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_COMMAND, &command_data, NULL, NULL)) != 0) {
406                                 LOG_E("error adding Command '%s' to dictionary: %s", commands[i]["Name"].asCString(), strerror(ret));
407                                 return false;
408                         }
409                 }
410                 LOG_D("Added Command '%s' to dictionary", commands[i]["Name"].asCString());
411         }
412 
413         return true;
414 }
415 
416 static bool
look_up_avp(const Json::Value & avp,const Json::Value & vendor,struct dict_object ** avp_object)417 look_up_avp(const Json::Value &avp, const Json::Value &vendor, struct dict_object **avp_object)
418 {
419         if (vendor != Json::Value::null) {
420                 struct dict_avp_request_ex dare;
421                 memset(&dare, 0, sizeof(dare));
422                 if (vendor.isInt()) {
423                         LOG_D("Looking for AVP '%s', Vendor %d", avp.asCString(), vendor.asInt());
424                         dare.avp_vendor.vendor_id = vendor.asUInt();
425                 } else if (vendor.isString()) {
426                         LOG_D("Looking for AVP '%s', Vendor '%s'", avp.asCString(), vendor.asCString());
427                         dare.avp_vendor.vendor_name = vendor.asCString();
428                 } else {
429                         LOG_E("error finding AVP '%s', invalid value for Vendor", avp.asCString());
430                         return false;
431                 }
432                 dare.avp_data.avp_name = avp.asCString();
433                 fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_STRUCT, &dare, avp_object, 0);
434         } else {
435                 fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, (void *)avp.asCString(), avp_object, 0);
436         }
437         if (*avp_object == NULL) {
438                 LOG_E("AVP '%s' not found", avp.asCString());
439                 return false;
440         }
441         return true;
442 }
443 
444 static bool
add_rule_entry(const Json::Value & rule,struct dict_object * parent)445 add_rule_entry(const Json::Value &rule, struct dict_object *parent)
446 {
447         struct dict_rule_data data;
448         int ret;
449 
450         memset(&data, 0, sizeof(data));
451         data.rule_min = -1;
452         if (rule["Min"] != Json::Value::null) {
453                 if (rule["Min"].isInt())
454                         data.rule_min = rule["Min"].asInt();
455                 else if (rule["Min"].isString() && strcmp(rule["Min"].asCString(), "unbounded") == 0)
456                         data.rule_min = -1;
457                 else {
458                         LOG_E("error adding rule with AVP '%s', invalid value for Min", rule["AVP"].asCString());
459                 }
460                 data.rule_min = rule["Min"].asInt();
461         }
462         if (data.rule_min > 0) {
463                 data.rule_position = RULE_REQUIRED;
464         } else {
465                 data.rule_position = RULE_OPTIONAL;
466         }
467         data.rule_max = -1;
468         if (rule["Max"] != Json::Value::null) {
469                 if (rule["Max"].isInt())
470                         data.rule_max = rule["Max"].asInt();
471                 else if (rule["Max"].isString() && strcmp(rule["Max"].asCString(), "unbounded") == 0)
472                         data.rule_max = -1;
473                 else {
474                         LOG_E("error adding rule with AVP '%s', invalid value for Max", rule["AVP"].asCString());
475                         return false;
476                 }
477         }
478         if (rule["First"] == true) {
479                 data.rule_position = RULE_FIXED_HEAD;
480                 data.rule_order = 1;
481         }
482 
483         LOG_D("Looking up AVP '%s' for rule", rule["AVP"].asCString());
484         if (!look_up_avp(rule["AVP"], rule["Vendor"], &data.rule_avp)) {
485                 LOG_E("Adding rule: AVP '%s' not found", rule["AVP"].asCString());
486                 return false;
487         }
488         LOG_D("Found AVP '%s' for rule", rule["AVP"].asCString());
489         if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_RULE, &data, parent, NULL)) < 0) {
490                 LOG_E("error adding rule with AVP '%s': %s", rule["AVP"].asCString(), strerror(ret));
491                 return false;
492         }
493         LOG_D("Added AVP '%s' to rule", rule["AVP"].asCString());
494         return true;
495 }
496 
497 
498 static bool
add_command_rule(const Json::Value & rule)499 add_command_rule(const Json::Value &rule)
500 {
501         int ret;
502         struct dict_object *command;
503 
504         if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, rule["Command"].asCString(), &command, ENOENT)) < 0) {
505                 LOG_E("Command '%s' not found in dictionary: %s", rule["Command"].asCString(), strerror(ret));
506                 return false;
507         }
508 
509         Json::Value content = rule["Content"];
510         if (content == Json::Value::null) {
511                 LOG_E("No rules for Command '%s'", rule["Command"].asCString());
512                 return false;
513         }
514         for (Json::ArrayIndex i=0; i<content.size(); i++) {
515                 if (!add_rule_entry(content[i], command)) {
516                         LOG_E("error adding command rule to dictionary");
517                         return false;
518                 }
519         }
520         LOG_D("Added rules for Command '%s'", rule["Command"].asCString());
521 
522         return true;
523 }
524 
525 static bool
add_avp_rule(const Json::Value & rule)526 add_avp_rule(const Json::Value &rule)
527 {
528         struct dict_object *avp;
529         struct dict_avp_data user_name_data;
530 
531         if (!look_up_avp(rule["AVP"], rule["Vendor"], &avp)) {
532                 LOG_E("AVP '%s' not found in dictionary", rule["AVP"].asCString());
533                 return false;
534         }
535 
536         if (fd_dict_getval(avp, &user_name_data) < 0) {
537                 LOG_E("Cannot get information for AVP '%s' from dictionary", rule["AVP"].asCString());
538                 return false;
539         }
540         if (user_name_data.avp_basetype != AVP_TYPE_GROUPED) {
541                 LOG_E("Invalid type for AVP '%s': cannot define rule for non-Grouped AVP", rule["AVP"].asCString());
542                 return false;
543         }
544 
545         Json::Value content = rule["Content"];
546         if (content == Json::Value::null) {
547                 LOG_E("No rules for AVP '%s'", rule["AVP"].asCString());
548                 return false;
549         }
550         for (Json::ArrayIndex i=0; i<content.size(); i++) {
551                 if (!add_rule_entry(content[i], avp)) {
552                         LOG_E("error adding AVP rule to dictionary");
553                         return false;
554                 }
555         }
556         LOG_D("Added rules for AVP '%s'", rule["AVP"].asCString());
557 
558         return true;
559 }
560 
561 static bool
add_command_rules(const Json::Value & config)562 add_command_rules(const Json::Value &config)
563 {
564         Json::Value commandrules = config["CommandRules"];
565         if (commandrules == Json::Value::null)
566                 return true;
567         for (Json::ArrayIndex i=0; i<commandrules.size(); i++) {
568                 if (!add_command_rule(commandrules[i])) {
569                         LOG_E("error adding command rules to dictionary");
570                         return false;
571                 }
572         }
573 
574         return true;
575 }
576 
577 static bool
add_avp_rules(const Json::Value & config)578 add_avp_rules(const Json::Value &config)
579 {
580         Json::Value avprules = config["AVPRules"];
581         if (avprules == Json::Value::null)
582                 return true;
583         for (Json::ArrayIndex i=0; i<avprules.size(); i++) {
584                 if (!add_avp_rule(avprules[i])) {
585                         LOG_E("error adding AVP rules to dictionary");
586                         return false;
587                 }
588         }
589 
590         return true;
591 }
592 
593 struct base_types_map {
594         std::string name;
595         enum dict_avp_basetype value;
596 };
597 struct base_types_map base_types[] = {
598         { "OctetString", AVP_TYPE_OCTETSTRING },
599         { "Integer32", AVP_TYPE_INTEGER32 },
600         { "Integer64", AVP_TYPE_INTEGER64 },
601         { "Unsigned32", AVP_TYPE_UNSIGNED32 },
602         { "Enumerated", AVP_TYPE_INTEGER32 },
603         { "Unsigned64", AVP_TYPE_UNSIGNED64 },
604         { "Float32", AVP_TYPE_FLOAT32 },
605         { "Float64", AVP_TYPE_FLOAT64 }
606 };
607 
608 static int
basic_type(std::string name)609 basic_type(std::string name) {
610         for (unsigned int i=0; i<sizeof(base_types)/sizeof(base_types[0]); i++) {
611                 if (name == base_types[i].name)
612                         return base_types[i].value;
613         }
614         return -1;
615 }
616 
617 static bool
add_types(const Json::Value & config)618 add_types(const Json::Value &config)
619 {
620         Json::Value types = config["Types"];
621         if (types == Json::Value::null)
622                 return true;
623         for (Json::ArrayIndex i=0; i<types.size(); i++) {
624                 int ret;
625                 struct dict_type_data type_data;
626                 struct dict_object *base_type;
627                 struct dict_type_data base_data;
628 
629                 ret = fd_dict_search(fd_g_config->cnf_dict, DICT_TYPE, TYPE_BY_NAME, types[i]["Base"].asCString(), &base_type, ENOENT);
630                 switch (ret) {
631                 case 0:
632                         if (fd_dict_getval(base_type, &base_data) < 0) {
633                                 LOG_E("Error looking up base type '%s' for type '%s'", types[i]["Base"].asCString(), types[i]["Name"].asCString());
634                                 return false;
635                         }
636                         type_data.type_base = base_data.type_base;
637                         break;
638 
639                 case ENOENT:
640                         /* not an extended type, perhaps a basic one? */
641                         ret = basic_type(types[i]["Base"].asString());
642                         if (ret != -1) {
643                                 type_data.type_base = (enum dict_avp_basetype)ret;
644                                 break;
645                         }
646                         /* fallthrough */
647 
648                 default:
649                         /* not found, or error */
650                         LOG_E("Base type '%s' for type '%s' not found", types[i]["Base"].asCString(), types[i]["Name"].asCString());
651                         return false;
652                 }
653 
654                 type_data.type_name = (char *)(void *)types[i]["Name"].asCString();
655                 if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_TYPE, &type_data, NULL, NULL)) != 0) {
656                         LOG_E("error adding Type '%s' to dictionary: %s", types[i]["Name"].asCString(), strerror(ret));
657                         return false;
658                 }
659                 LOG_D("Added Type '%s' to dictionary", types[i]["Name"].asCString());
660         }
661 
662         return true;
663 }
664 
665 static bool
add_vendors(const Json::Value & config)666 add_vendors(const Json::Value &config)
667 {
668         Json::Value vendors = config["Vendors"];
669         if (vendors == Json::Value::null)
670                 return true;
671         for (Json::ArrayIndex i=0; i<vendors.size(); i++) {
672                 int ret;
673                 struct dict_vendor_data vendor_data;
674                 vendor_data.vendor_id = vendors[i]["Code"].asUInt();
675                 vendor_data.vendor_name = (char *)(void *)vendors[i]["Name"].asCString();
676                 if ((ret=fd_dict_new(fd_g_config->cnf_dict, DICT_VENDOR, &vendor_data, NULL, NULL)) != 0) {
677                         LOG_E("error adding Vendor '%s' to dictionary: %s", vendors[i]["Name"].asCString(), strerror(ret));
678                         return false;
679                 }
680                 LOG_D("Added Vendor '%s' to dictionary", vendors[i]["Name"].asCString());
681         }
682 
683         return true;
684 }
685 
686 static bool
parse_json_from_file(const char * conffile,Json::Value & jv)687 parse_json_from_file(const char *conffile, Json::Value &jv)
688 {
689         struct stat sb;
690         char *buf;
691         FILE *fp;
692         Json::Reader reader;
693         static Json::SchemaValidator *validator = NULL;
694 
695         if (conffile == NULL || stat(conffile, &sb) < 0 || !S_ISREG(sb.st_mode)) {
696                 LOG_E("invalid or missing configuration: %s", conffile ?: "(null)");
697                 return false;
698         }
699         if ((buf=(char *)malloc(static_cast<size_t>(sb.st_size + 1))) == NULL) {
700                 LOG_E("malloc failure of %zu bytes", static_cast<size_t>(sb.st_size));
701                 return false;
702         }
703         if ((fp=fopen(conffile, "r")) == NULL) {
704                 LOG_E("error opening file '%s': %s", conffile, strerror(errno));
705                 return false;
706         }
707         if (fread(buf, static_cast<size_t>(sb.st_size), 1, fp) != 1) {
708                 LOG_E("error reading from file '%s': %s", conffile, strerror(errno));
709                 return false;
710         }
711         (void)fclose(fp);
712 
713         buf[sb.st_size] = '\0';
714 
715         if (!reader.parse (buf, jv)) {
716                 LOG_E("error parsing JSON data from file '%s': %s", conffile, reader.getFormattedErrorMessages().c_str());
717                 return false;
718         }
719         free(buf);
720 
721         if (validator == NULL) {
722                 try {
723                         validator = new Json::SchemaValidator(std::string(dict_json_dict_schema));
724                 } catch (Json::SchemaValidator::Exception &e) {
725                         LOG_E("error creating JSON schema validator: %s", e.type_message().c_str());
726                         return false;
727                 }
728         }
729 
730         if (!validator->validate(jv)) {
731                 LOG_E("error validating config file %s:", conffile);
732                 const std::vector<Json::SchemaValidator::Error> &errors = validator->errors();
733                 for (std::vector<Json::SchemaValidator::Error>::const_iterator it = errors.begin(); it != errors.end(); ++it) {
734                         LOG_E("  %s:%s", it->path.c_str(), it->message.c_str());
735                 }
736                 return false;
737         }
738 
739 #if 0
740         Json::StyledWriter styledWriter;
741         std::cout << styledWriter.write(jv);
742 #endif
743 
744         return true;
745 }
746 
747 static bool
read_dictionary(const char * filename)748 read_dictionary(const char *filename)
749 {
750         Json::Value config = Json::Value::null;
751         if (!parse_json_from_file (filename, config)) {
752                 LOG_E("error parsing JSON file");
753                 return false;
754         }
755 
756         if (!add_vendors(config)) {
757                 LOG_E("error adding vendors");
758                 return false;
759         }
760         if (!add_types(config)) {
761                 LOG_E("error adding types");
762                 return false;
763         }
764         if (!add_avps(config)) {
765                 LOG_E("error adding AVPs");
766                 return false;
767         }
768         if (!add_applications(config)) {
769                 LOG_E("error adding applications");
770                 return false;
771         }
772         if (!add_commands(config)) {
773                 LOG_E("error adding commands");
774                 return false;
775         }
776         if (!add_command_rules(config)) {
777                 LOG_E("error adding command rules");
778                 return false;
779         }
780         if (!add_avp_rules(config)) {
781                 LOG_E("error adding AVP rules");
782                 return false;
783         }
784 
785         return true;
786 }
787 
788 static int
dict_json_entry(char * conffile)789 dict_json_entry(char * conffile)
790 {
791         Json::Value main_config = Json::Value::null;
792         char *filename, *filename_base, *p;
793 
794         TRACE_ENTRY("%p", conffile);
795 
796         filename_base = strdup(conffile);
797         if (filename_base == NULL) {
798                 LOG_E("error initialising JSON dictionary extension: %s", strerror(errno));
799                 return 1;
800         }
801         filename = filename_base;
802         while ((p=strsep(&filename, ";")) != NULL) {
803                 LOG_D("parsing dictionary '%s'", p);
804                 if (!read_dictionary(p)) {
805                         LOG_E("error reading JSON dictionary '%s'", p);
806                         return EINVAL;
807                 }
808                 LOG_N("loaded JSON dictionary '%s'", p);
809                 if (filename == NULL)
810                         break;
811         }
812 
813         free(filename_base);
814         LOG_N("Extension 'Dictionary definitions from JSON dictionaries' initialized");
815         return 0;
816 }
817 
818 extern "C" {
819         EXTENSION_ENTRY("dict_json", dict_json_entry);
820 }
821