diff options
Diffstat (limited to 'epan/dissectors/packet-epl-profile-parser.c')
-rw-r--r-- | epan/dissectors/packet-epl-profile-parser.c | 548 |
1 files changed, 535 insertions, 13 deletions
diff --git a/epan/dissectors/packet-epl-profile-parser.c b/epan/dissectors/packet-epl-profile-parser.c index 771a395009..7ac6d2a1e3 100644 --- a/epan/dissectors/packet-epl-profile-parser.c +++ b/epan/dissectors/packet-epl-profile-parser.c @@ -39,12 +39,100 @@ #include <string.h> #include <stdlib.h> +#include <wsutil/strtoi.h> +#include <wsutil/str_util.h> #include <epan/wmem/wmem.h> -/* XXX: Temporary. successive related change makes use of the functions here */ -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wunused-function" -#endif +#if defined HAVE_LIBXML2 +#include <libxml/xmlversion.h> + +#if defined LIBXML_XPATH_ENABLED \ +&& defined LIBXML_SAX1_ENABLED \ +&& defined LIBXML_TREE_ENABLED +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> + +#define PARSE_XDD 1 + +typedef int xpath_handler(xmlNodeSetPtr, void*); +static xpath_handler populate_object_list, populate_datatype_list, populate_profile_name; + +static struct xpath_namespace { + const xmlChar *prefix, *href; +} namespaces[] = { + { BAD_CAST "x", BAD_CAST "http://www.ethernet-powerlink.org" }, + { BAD_CAST "xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance" }, + { NULL, NULL } +}; + +static struct xpath { + const xmlChar *expr; + xpath_handler *handler; +} xpaths[] = { + { + BAD_CAST "//x:ISO15745Profile[x:ProfileHeader/x:ProfileIdentification='Powerlink_Communication_Profile']/x:ProfileHeader/x:ProfileName", + populate_profile_name + }, + { + BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:DataTypeList/x:defType", + populate_datatype_list + }, + { + BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:ObjectList/x:Object", + populate_object_list + }, + { NULL, NULL } +}; + + +#endif /* LIBXML_XPATH_ENABLED && LIBXML_SAX1_ENABLED && LIBXML_TREE_ENABLED */ + +#endif /* HAVE_LIBXML2 */ + +struct datatype { + guint16 id; + const struct epl_datatype *ptr; +}; + +static struct typemap_entry { + guint16 id; + const char *name; + struct epl_datatype *type; +} epl_datatypes[] = { + {0x0001, "Boolean", NULL}, + {0x0002, "Integer8", NULL}, + {0x0003, "Integer16", NULL}, + {0x0004, "Integer32", NULL}, + {0x0005, "Unsigned8", NULL}, + {0x0006, "Unsigned16", NULL}, + {0x0007, "Unsigned32", NULL}, + {0x0008, "Real32", NULL}, + {0x0009, "Visible_String", NULL}, + {0x0010, "Integer24", NULL}, + {0x0011, "Real64", NULL}, + {0x0012, "Integer40", NULL}, + {0x0013, "Integer48", NULL}, + {0x0014, "Integer56", NULL}, + {0x0015, "Integer64", NULL}, + {0x000A, "Octet_String", NULL}, + {0x000B, "Unicode_String", NULL}, + {0x000C, "Time_of_Day", NULL}, + {0x000D, "Time_Diff", NULL}, + {0x000F, "Domain", NULL}, + {0x0016, "Unsigned24", NULL}, + {0x0018, "Unsigned40", NULL}, + {0x0019, "Unsigned48", NULL}, + {0x001A, "Unsigned56", NULL}, + {0x001B, "Unsigned64", NULL}, + {0x0401, "MAC_ADDRESS", NULL}, + {0x0402, "IP_ADDRESS", NULL}, + {0x0403, "NETTIME", NULL}, + {0x0000, NULL, NULL} +}; + +static wmem_map_t *eds_typemap; struct epl_wmem_iarray { GEqualFunc equal; @@ -55,12 +143,446 @@ struct epl_wmem_iarray { }; static epl_wmem_iarray_t *epl_wmem_iarray_new(wmem_allocator_t *allocator, const guint elem_size, GEqualFunc cmp) G_GNUC_MALLOC; -static gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr); -static gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr); +gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr); +gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr); static void epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data); static void epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr); -static range_admin_t * epl_wmem_iarray_find(epl_wmem_iarray_t *arr, guint32 value); +static gboolean +epl_ishex(const char *num) +{ + if (g_str_has_prefix(num, "0x")) + return TRUE; + + for (; g_ascii_isxdigit(*num); num++) + ; + + if (g_ascii_tolower(*num) == 'h') + return TRUE; + + return FALSE; +} + +static guint16 +epl_g_key_file_get_uint16(GKeyFile *gkf, const gchar *group_name, const gchar *key, GError **error) +{ + guint16 ret = 0; + const char *endptr; + char *val = g_key_file_get_string(gkf, group_name, key, error); + + if (!val) + return 0; + + if (epl_ishex(val)) /* We need to support XXh, but no octals (is that right?) */ + ws_hexstrtou16(val, &endptr, &ret); + else + ws_strtou16(val, &endptr, &ret); + + g_free(val); + return ret; +} + +static void +sort_subindices(void *key _U_, void *value, void *user_data _U_) +{ + epl_wmem_iarray_t *subindices = ((struct object*)value)->subindices; + if (subindices) + epl_wmem_iarray_sort_and_compact(subindices); +} + +void +epl_eds_init(void) +{ + struct typemap_entry *entry; + eds_typemap = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal); + for (entry = epl_datatypes; entry->name; entry++) + { + const struct epl_datatype *type = epl_type_to_hf(entry->name); + wmem_map_insert(eds_typemap, GUINT_TO_POINTER(entry->id), (void*)type); + } +} + +struct profile * +epl_eds_load(struct profile *profile, const char *eds_file) +{ + GKeyFile* gkf; + GError *err; + char **group, **groups; + char *val; + gsize groups_count; + + gkf = g_key_file_new(); + + /* Load EDS document */ + if (!g_key_file_load_from_file(gkf, eds_file, G_KEY_FILE_NONE, &err)){ + g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", eds_file); + profile = NULL; + goto cleanup; + } + + profile->path = wmem_strdup(profile->scope, eds_file); + + val = g_key_file_get_string(gkf, "FileInfo", "Description", NULL); + /* This leaves a trailing space, but that's ok */ + profile->name = wmem_strndup(profile->scope, val, strcspn(val, "#")); + g_free(val); + + groups = g_key_file_get_groups(gkf, &groups_count); + for (group = groups; *group; group++) + { + char *name; + const char *endptr; + guint16 idx = 0, datatype; + struct object *obj = NULL; + struct od_entry tmpobj = {0}; + gboolean is_object = TRUE; + + if (!g_ascii_isxdigit(**group)) + continue; + + ws_hexstrtou16(*group, &endptr, &idx); + if (*endptr == '\0') + { /* index */ + tmpobj.idx = idx; + } + else if (g_str_has_prefix(endptr, "sub")) + { /* subindex */ + if (!ws_hexstrtou16(endptr + 3, &endptr, &tmpobj.idx) + || tmpobj.idx > 0xFF) + continue; + + is_object = FALSE; + } + else continue; + + tmpobj.type_class = epl_g_key_file_get_uint16(gkf, *group, "ObjectType", NULL); + if (!tmpobj.type_class) + continue; + + datatype = epl_g_key_file_get_uint16(gkf, *group, "DataType", NULL); + if (datatype) + tmpobj.type = (const struct epl_datatype*)wmem_map_lookup(eds_typemap, GUINT_TO_POINTER(datatype)); + + if ((name = g_key_file_get_string(gkf, *group, "ParameterName", NULL))) + { + gsize count = strcspn(name, "#") + 1; + g_strlcpy( + tmpobj.name, + name, + count > sizeof tmpobj.name ? sizeof tmpobj.name : count + ); + g_free(name); + } + + obj = epl_profile_object_lookup_or_add(profile, idx); + + if (is_object) + { /* Let's add a new object! Exciting! */ + obj->info = tmpobj; + } + else + { /* Object already there, let's add subindices */ + struct subobject subobj = {0}; + if (!obj->subindices) + { + obj->subindices = epl_wmem_iarray_new( + profile->scope, + sizeof (struct subobject), + subobject_equal + ); + } + + subobj.info = tmpobj; + epl_wmem_iarray_insert(obj->subindices, subobj.info.idx, &subobj.range); + } + } + + /* Unlike with XDDs, subindices might interleave with others, so let's sort them now */ + wmem_map_foreach(profile->objects, sort_subindices, NULL); + + /* We don't read object mappings from EDS files */ + /* epl_profile_object_mappings_update(profile); */ + +cleanup: + g_key_file_free(gkf); + return profile; +} + +#ifdef PARSE_XDD + +void +epl_xdd_init(void) +{ +} + +struct profile * +epl_xdd_load(struct profile *profile, const char *xml_file) +{ + xmlXPathContextPtr xpathCtx = NULL; + xmlDoc *doc = NULL; + struct xpath_namespace *ns = NULL; + struct xpath *xpath = NULL; + GHashTable *typemap = NULL; + + /* Load XML document */ + doc = xmlParseFile(xml_file); + if (!doc) + { + g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", xml_file); + profile = NULL; + goto cleanup; + } + + + /* Create xpath evaluation context */ + xpathCtx = xmlXPathNewContext(doc); + if(!xpathCtx) + { + g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to create new XPath context\n"); + profile = NULL; + goto cleanup; + } + + /* Register namespaces from list */ + for (ns = namespaces; ns->href; ns++) + { + if(xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href) != 0) + { + g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href); + profile = NULL; + goto cleanup; + } + } + + profile->path = wmem_strdup(profile->scope, xml_file); + + /* mapping type ids to &hf_s */ + profile->data = typemap = (GHashTable*)g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + /* Evaluate xpath expressions */ + for (xpath = xpaths; xpath->expr; xpath++) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpath->expr, xpathCtx); + if (!xpathObj || !xpathObj->nodesetval) + { + g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to evaluate xpath expression \"%s\"\n", xpath->expr); + xmlXPathFreeObject(xpathObj); + profile = NULL; + goto cleanup; + } + + /* run handler */ + if (xpath->handler && xpathObj->nodesetval->nodeNr) + xpath->handler(xpathObj->nodesetval, profile); + xmlXPathFreeObject(xpathObj); + } + + /* We create ObjectMappings while reading the XML, this is makes it likely, + * that we won't be able to reference a mapped object in the ObjectMapping + * as we didn't reach its XML tag yet. Therefore, after reading the XDD + * completely, we update mappings in the profile + */ + epl_profile_object_mappings_update(profile); + +cleanup: + if (typemap) + g_hash_table_destroy(typemap); + + if (xpathCtx) + xmlXPathFreeContext(xpathCtx); + if (doc) + xmlFreeDoc(doc); + + return profile; +} + +static int +populate_profile_name(xmlNodeSetPtr nodes, void *_profile) +{ + struct profile *profile = (struct profile*)_profile; + if (nodes->nodeNr == 1 + && nodes->nodeTab[0]->type == XML_ELEMENT_NODE + && nodes->nodeTab[0]->children) + { + profile->name = wmem_strdup(profile->scope, (char*)nodes->nodeTab[0]->children->content); + return 0; + } + + return -1; +} + +static int +populate_datatype_list(xmlNodeSetPtr nodes, void *_profile) +{ + xmlNodePtr cur; + int i; + struct profile *profile = (struct profile*)_profile; + + for(i = 0; i < nodes->nodeNr; ++i) + { + xmlAttrPtr attr; + + if(!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE) + return -1; + + cur = nodes->nodeTab[i]; + + + for(attr = cur->properties; attr; attr = attr->next) + { + const char *endptr; + const char *key = (const char*)attr->name; + const char *val = (const char*)attr->children->content; + + if (g_str_equal("dataType", key)) + { + xmlNode *subnode; + guint16 idx = 0; + + if (!ws_hexstrtou16(val, &endptr, &idx)) + continue; + + for (subnode = cur->children; subnode; subnode = subnode->next) + { + if (subnode->type == XML_ELEMENT_NODE) + { + struct datatype *type; + const struct epl_datatype *ptr = epl_type_to_hf((char*)subnode->name); + if (!ptr) + { + g_log(NULL, G_LOG_LEVEL_INFO, "Skipping unknown type '%s'\n", subnode->name); + continue; + } + type = g_new(struct datatype, 1); + type->id = idx; + type->ptr = ptr; + g_hash_table_insert((GHashTable*)profile->data, GUINT_TO_POINTER(type->id), type); + continue; + } + } + + } + } + } + + return 0; +} + +static gboolean +parse_obj_tag(xmlNode *cur, struct od_entry *out, struct profile *profile) { + xmlAttrPtr attr; + const char *defaultValue = NULL, *actualValue = NULL; + const char *endptr; + + for(attr = cur->properties; attr; attr = attr->next) + { + const char *key = (char*)attr->name, + *val = (char*)attr->children->content; + + if (g_str_equal("index", key)) + { + if (!ws_hexstrtou16(val, &endptr, &out->idx)) + return FALSE; + + } else if (g_str_equal("subIndex", key)) { + if (!ws_hexstrtou16(val, &endptr, &out->idx)) + return FALSE; + + } else if (g_str_equal("name", key)) { + g_strlcpy(out->name, val, sizeof out->name); + + } else if (g_str_equal("objectType", key)) { + out->type_class = 0; + ws_hexstrtou16(val, &endptr, &out->type_class); + + } else if (g_str_equal("dataType", key)) { + guint16 id; + if (ws_hexstrtou16(val, &endptr, &id)) + { + struct datatype *type = (struct datatype*)g_hash_table_lookup((GHashTable*)profile->data, GUINT_TO_POINTER(id)); + if (type) out->type = type->ptr; + } + + } else if (g_str_equal("defaultValue", key)) { + defaultValue = val; + + } else if (g_str_equal("actualValue", key)) { + actualValue = val; + } +#if 0 + else if (g_str_equal("PDOmapping", key)) { + obj.PDOmapping = get_index(ObjectPDOmapping_tostr, val); + assert(obj.PDOmapping >= 0); + } +#endif + } + + if (actualValue) + out->value = g_ascii_strtoull(actualValue, NULL, 0); + else if (defaultValue) + out->value = g_ascii_strtoull(defaultValue, NULL, 0); + else + out->value = 0; + + + return TRUE; +} + +static int +populate_object_list(xmlNodeSetPtr nodes, void *_profile) +{ + int i; + struct profile *profile = (struct profile*)_profile; + + for(i = 0; i < nodes->nodeNr; ++i) + { + xmlNodePtr cur = nodes->nodeTab[i]; + struct od_entry tmpobj = {0}; + + if (!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE) + continue; + + parse_obj_tag(cur, &tmpobj, profile); + + if (tmpobj.idx) + { + struct object *obj = epl_profile_object_add(profile, tmpobj.idx); + obj->info = tmpobj; + + if (tmpobj.type_class == OD_ENTRY_ARRAY || tmpobj.type_class == OD_ENTRY_RECORD) + { + xmlNode *subcur; + struct subobject subobj = {0}; + + obj->subindices = epl_wmem_iarray_new(profile->scope, sizeof (struct subobject), subobject_equal); + + for (subcur = cur->children; subcur; subcur = subcur->next) + { + if (subcur->type != XML_ELEMENT_NODE) + continue; + + if (parse_obj_tag(subcur, &subobj.info, profile)) + { + epl_wmem_iarray_insert(obj->subindices, + subobj.info.idx, &subobj.range); + } + if (subobj.info.value && epl_profile_object_mapping_add( + profile, obj->info.idx, (guint8)subobj.info.idx, subobj.info.value)) + { + g_log(NULL, G_LOG_LEVEL_INFO, + "Loaded mapping from XDC %s:%s", obj->info.name, subobj.info.name); + } + } + epl_wmem_iarray_sort_and_compact(obj->subindices); + } + } + } + + return 0; +} + + +#endif /* PARSE_XDD */ /** * A sorted array keyed by intervals @@ -104,7 +626,7 @@ free_garray(wmem_allocator_t *scope _U_, wmem_cb_event_t event _U_, void *data) * it's always the second argument that's getting removed. */ -epl_wmem_iarray_t * +static epl_wmem_iarray_t * epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc equal) { epl_wmem_iarray_t *iarr; @@ -126,21 +648,21 @@ epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc e /** Returns true if the iarr is empty. */ -static gboolean +gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr) { return iarr->arr->len == 0; } /** Returns true if the iarr is sorted. */ -static gboolean +gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr) { return iarr->is_sorted; } /** Inserts an element */ -void +static void epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data) { if (iarr->arr->len) @@ -157,7 +679,7 @@ epl_wmem_iarray_cmp(const void *a, const void *b) } /** Makes array suitable for searching */ -void +static void epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr) { range_admin_t *elem, *prev = NULL; @@ -207,7 +729,7 @@ bsearch_garray(const void *key, GArray *arr, int (*cmp)(const void*, const void* * Finds an element in the interval array. Returns NULL if it doesn't exist * Calling this is unspecified if the array wasn't sorted before */ -static range_admin_t * +range_admin_t * epl_wmem_iarray_find(epl_wmem_iarray_t *iarr, guint32 value) { epl_wmem_iarray_sort_and_compact(iarr); |