/* This file is part of GNUnet. Copyright (C) 2009-2013 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /** * @file gnsrecord/gnsrecord.c * @brief API to access GNS record data * @author Martin Schanzenbach * @author Matthias Wachs * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_constants.h" #include "gnunet_gnsrecord_lib.h" #include "gnunet_gnsrecord_plugin.h" #include "gnunet_json_lib.h" #include "gnunet_tun_lib.h" #include #define LOG(kind,...) GNUNET_log_from (kind, "gnsrecord",__VA_ARGS__) /** * Handle for a plugin. */ struct Plugin { /** * Name of the shared library. */ char *library_name; /** * Plugin API. */ struct GNUNET_GNSRECORD_PluginFunctions *api; }; /** * Array of our plugins. */ static struct Plugin **gns_plugins; /** * Size of the 'plugins' array. */ static unsigned int num_plugins; /** * Global to mark if we've run the initialization. */ static int once; /** * Add a plugin to the list managed by the block library. * * @param cls NULL * @param library_name name of the plugin * @param lib_ret the plugin API */ static void add_plugin (void *cls, const char *library_name, void *lib_ret) { struct GNUNET_GNSRECORD_PluginFunctions *api = lib_ret; struct Plugin *plugin; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading block plugin `%s'\n", library_name); plugin = GNUNET_new (struct Plugin); plugin->api = api; plugin->library_name = GNUNET_strdup (library_name); GNUNET_array_append (gns_plugins, num_plugins, plugin); } /** * Loads all plugins (lazy initialization). */ static void init () { if (1 == once) return; once = 1; GNUNET_PLUGIN_load_all ("libgnunet_plugin_gnsrecord_", NULL, &add_plugin, NULL); } /** * Dual function to #init(). */ void __attribute__ ((destructor)) GNSRECORD_fini () { struct Plugin *plugin; for (unsigned int i = 0; i < num_plugins; i++) { plugin = gns_plugins[i]; GNUNET_break (NULL == GNUNET_PLUGIN_unload (plugin->library_name, plugin->api)); GNUNET_free (plugin->library_name); GNUNET_free (plugin); } GNUNET_free_non_null (gns_plugins); gns_plugins = NULL; once = 0; num_plugins = 0; } /** * Convert the 'value' of a record to a string. * * @param type type of the record * @param data value in binary encoding * @param data_size number of bytes in @a data * @return NULL on error, otherwise human-readable representation of the value */ char * GNUNET_GNSRECORD_value_to_string (uint32_t type, const void *data, size_t data_size) { struct Plugin *plugin; char *ret; init (); for (unsigned int i = 0; i < num_plugins; i++) { plugin = gns_plugins[i]; if (NULL != (ret = plugin->api->value_to_string (plugin->api->cls, type, data, data_size))) return ret; } return NULL; } /** * Convert human-readable version of a 'value' of a record to the binary * representation. * * @param type type of the record * @param s human-readable string * @param data set to value in binary encoding (will be allocated) * @param data_size set to number of bytes in @a data * @return #GNUNET_OK on success */ int GNUNET_GNSRECORD_string_to_value (uint32_t type, const char *s, void **data, size_t *data_size) { struct Plugin *plugin; init (); for (unsigned int i = 0; i < num_plugins; i++) { plugin = gns_plugins[i]; if (GNUNET_OK == plugin->api->string_to_value (plugin->api->cls, type, s, data, data_size)) return GNUNET_OK; } return GNUNET_SYSERR; } /** * Convert a type name (i.e. "AAAA") to the corresponding number. * * @param dns_typename name to convert * @return corresponding number, UINT32_MAX on error */ uint32_t GNUNET_GNSRECORD_typename_to_number (const char *dns_typename) { struct Plugin *plugin; uint32_t ret; if (0 == strcasecmp (dns_typename, "ANY")) return GNUNET_GNSRECORD_TYPE_ANY; init (); for (unsigned int i = 0; i < num_plugins; i++) { plugin = gns_plugins[i]; if (UINT32_MAX != (ret = plugin->api->typename_to_number (plugin->api->cls, dns_typename))) return ret; } return UINT32_MAX; } /** * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A") * * @param type number of a type to convert * @return corresponding typestring, NULL on error */ const char * GNUNET_GNSRECORD_number_to_typename (uint32_t type) { struct Plugin *plugin; const char * ret; if (GNUNET_GNSRECORD_TYPE_ANY == type) return "ANY"; init (); for (unsigned int i = 0; i < num_plugins; i++) { plugin = gns_plugins[i]; if (NULL != (ret = plugin->api->number_to_typename (plugin->api->cls, type))) return ret; } return NULL; } /** * Parse given JSON object to gns record * * @param cls closure, NULL * @param root the json object representing data * @param spec where to write the data * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error */ static int parse_gnsrecordobject (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) { struct GNUNET_GNSRECORD_Data *gnsrecord_object; struct GNUNET_TIME_Absolute abs_expiration_time; int unpack_state=0; const char *data; const char *expiration_date; const char *record_type; const char *dummy_value; int flag; void *rdata; size_t rdata_size; if(!json_is_object(root)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error json is not array nor object!\n"); return GNUNET_SYSERR; } //interpret single gns record unpack_state = json_unpack(root, "{s:s, s:s, s:s, s?:i, s:s!}", "value", &data, "type", &record_type, "expiration_time", &expiration_date, "flag", &flag, "label", &dummy_value); GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "{value:%s, type:%s, expire:%s, flag:%i}", data, record_type, expiration_date, flag); if (GNUNET_SYSERR == unpack_state) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error json object has a wrong format!\n"); return GNUNET_SYSERR; } //TODO test gnsrecord_object = GNUNET_new (struct GNUNET_GNSRECORD_Data); gnsrecord_object->record_type = GNUNET_GNSRECORD_typename_to_number(record_type); if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (gnsrecord_object->record_type, data, &rdata, &rdata_size)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR,"Value invalid for record type"); return GNUNET_SYSERR; } gnsrecord_object->data = rdata; gnsrecord_object->data_size = rdata_size; if (0 == strcmp (expiration_date, "never")) { gnsrecord_object->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; } else if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_absolute (expiration_date, &abs_expiration_time)) { gnsrecord_object->expiration_time = abs_expiration_time.abs_value_us; } else { GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Value invalid for record type"); return GNUNET_SYSERR; } gnsrecord_object->flags = (enum GNUNET_GNSRECORD_Flags)flag; *(struct GNUNET_GNSRECORD_Data **) spec->ptr = gnsrecord_object; return GNUNET_OK; } /** * Cleanup data left from parsing RSA public key. * * @param cls closure, NULL * @param[out] spec where to free the data */ static void clean_gnsrecordobject (void *cls, struct GNUNET_JSON_Specification *spec) { struct GNUNET_GNSRECORD_Data **gnsrecord_object; gnsrecord_object = (struct GNUNET_GNSRECORD_Data **) spec->ptr; if (NULL != *gnsrecord_object) { if (NULL != (*gnsrecord_object)->data) GNUNET_free((char*)(*gnsrecord_object)->data); GNUNET_free(*gnsrecord_object); *gnsrecord_object = NULL; } } /** * JSON Specification for GNS Records. * * @param gnsrecord_object struct of GNUNET_GNSRECORD_Data to fill * @return JSON Specification */ struct GNUNET_JSON_Specification GNUNET_JSON_spec_gnsrecord_data (struct GNUNET_GNSRECORD_Data **gnsrecord_object) { struct GNUNET_JSON_Specification ret = { .parser = &parse_gnsrecordobject, .cleaner = &clean_gnsrecordobject, .cls = NULL, .field = NULL, .ptr = gnsrecord_object, .ptr_size = 0, .size_ptr = NULL }; *gnsrecord_object = NULL; return ret; } /* end of gnsrecord.c */