/* This file is part of GNUnet. Copyright (C) 2012, 2013, 2014 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file namestore/gnunet-service-namestore.c * @brief namestore for the GNUnet naming system * @author Matthias Wachs * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_dnsparser_lib.h" #include "gnunet_gns_service.h" #include "gnunet_namecache_service.h" #include "gnunet_namestore_service.h" #include "gnunet_namestore_plugin.h" #include "gnunet_statistics_service.h" #include "gnunet_signatures.h" #include "namestore.h" #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) /** * A namestore client */ struct NamestoreClient; /** * A namestore iteration operation. */ struct ZoneIteration { /** * Next element in the DLL */ struct ZoneIteration *next; /** * Previous element in the DLL */ struct ZoneIteration *prev; /** * Namestore client which intiated this zone iteration */ struct NamestoreClient *nc; /** * The nick to add to the records */ struct GNUNET_GNSRECORD_Data *nick; /** * Key of the zone we are iterating over. */ struct GNUNET_CRYPTO_EcdsaPrivateKey zone; /** * The operation id fot the zone iteration in the response for the client */ uint32_t request_id; /** * Offset of the zone iteration used to address next result of the zone * iteration in the store * * Initialy set to 0 in #handle_iteration_start * Incremented with by every call to #handle_iteration_next */ uint32_t offset; }; /** * A namestore client */ struct NamestoreClient { /** * The client */ struct GNUNET_SERVICE_Client *client; /** * Message queue for transmission to @e client */ struct GNUNET_MQ_Handle *mq; /** * Head of the DLL of * Zone iteration operations in progress initiated by this client */ struct ZoneIteration *op_head; /** * Tail of the DLL of * Zone iteration operations in progress initiated by this client */ struct ZoneIteration *op_tail; }; /** * A namestore monitor. */ struct ZoneMonitor { /** * Next element in the DLL */ struct ZoneMonitor *next; /** * Previous element in the DLL */ struct ZoneMonitor *prev; /** * Namestore client which intiated this zone monitor */ struct NamestoreClient *nc; /** * Private key of the zone. */ struct GNUNET_CRYPTO_EcdsaPrivateKey zone; /** * Task active during initial iteration. */ struct GNUNET_SCHEDULER_Task *task; /** * Offset of the zone iteration used to address next result of the zone * iteration in the store * * Initialy set to 0. * Incremented with by every call to #handle_iteration_next */ uint32_t offset; }; /** * Pending operation on the namecache. */ struct CacheOperation { /** * Kept in a DLL. */ struct CacheOperation *prev; /** * Kept in a DLL. */ struct CacheOperation *next; /** * Handle to namecache queue. */ struct GNUNET_NAMECACHE_QueueEntry *qe; /** * Client to notify about the result. */ struct NamestoreClient *nc; /** * Client's request ID. */ uint32_t rid; }; /** * Public key of all zeros. */ static const struct GNUNET_CRYPTO_EcdsaPrivateKey zero; /** * Configuration handle. */ static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg; /** * Handle to the statistics service */ static struct GNUNET_STATISTICS_Handle *statistics; /** * Namecache handle. */ static struct GNUNET_NAMECACHE_Handle *namecache; /** * Database handle */ static struct GNUNET_NAMESTORE_PluginFunctions *GSN_database; /** * Name of the database plugin */ static char *db_lib_name; /** * Head of cop DLL. */ static struct CacheOperation *cop_head; /** * Tail of cop DLL. */ static struct CacheOperation *cop_tail; /** * First active zone monitor. */ static struct ZoneMonitor *monitor_head; /** * Last active zone monitor. */ static struct ZoneMonitor *monitor_tail; /** * Notification context shared by all monitors. */ static struct GNUNET_NotificationContext *monitor_nc; /** * Task run during shutdown. * * @param cls unused */ static void cleanup_task (void *cls) { struct CacheOperation *cop; (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping namestore service\n"); while (NULL != (cop = cop_head)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting incomplete namecache operation\n"); GNUNET_NAMECACHE_cancel (cop->qe); GNUNET_CONTAINER_DLL_remove (cop_head, cop_tail, cop); GNUNET_free (cop); } GNUNET_NAMECACHE_disconnect (namecache); namecache = NULL; GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, GSN_database)); GNUNET_free (db_lib_name); db_lib_name = NULL; if (NULL != monitor_nc) { GNUNET_notification_context_destroy (monitor_nc); monitor_nc = NULL; } if (NULL != statistics) { GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); statistics = NULL; } } /** * Called whenever a client is disconnected. * Frees our resources associated with that client. * * @param cls closure * @param client identification of the client * @param app_ctx the `struct NamestoreClient` of @a client */ static void client_disconnect_cb (void *cls, struct GNUNET_SERVICE_Client *client, void *app_ctx) { struct NamestoreClient *nc = app_ctx; struct ZoneIteration *no; struct ZoneMonitor *zm; struct CacheOperation *cop; (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected\n", client); for (zm = monitor_head; NULL != zm; zm = zm->next) { if (nc == zm->nc) { GNUNET_CONTAINER_DLL_remove (monitor_head, monitor_tail, zm); if (NULL != zm->task) { GNUNET_SCHEDULER_cancel (zm->task); zm->task = NULL; } GNUNET_free (zm); break; } } while (NULL != (no = nc->op_head)) { GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, no); GNUNET_free (no); } for (cop = cop_head; NULL != cop; cop = cop->next) if (nc == cop->nc) cop->nc = NULL; GNUNET_free (nc); } /** * Add a client to our list of active clients. * * @param cls NULL * @param client client to add * @param mq message queue for @a client * @return internal namestore client structure for this client */ static void * client_connect_cb (void *cls, struct GNUNET_SERVICE_Client *client, struct GNUNET_MQ_Handle *mq) { struct NamestoreClient *nc; (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected\n", client); nc = GNUNET_new (struct NamestoreClient); nc->client = client; nc->mq = mq; return nc; } /** * Function called with the records for the #GNUNET_GNS_EMPTY_LABEL_AT * label in the zone. Used to locate the #GNUNET_GNSRECORD_TYPE_NICK * record, which (if found) is then copied to @a cls for future use. * * @param cls a `struct GNUNET_GNSRECORD_Data **` for storing the nick (if found) * @param private_key the private key of the zone (unused) * @param label should be #GNUNET_GNS_EMPTY_LABEL_AT * @param rd_count number of records in @a rd * @param rd records stored under @a label in the zone */ static void lookup_nick_it (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct GNUNET_GNSRECORD_Data **res = cls; (void) private_key; if (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT)) { GNUNET_break (0); return; } for (unsigned int c = 0; c < rd_count; c++) { if (GNUNET_GNSRECORD_TYPE_NICK == rd[c].record_type) { (*res) = GNUNET_malloc (rd[c].data_size + sizeof (struct GNUNET_GNSRECORD_Data)); (*res)->data = &(*res)[1]; GNUNET_memcpy ((void *) (*res)->data, rd[c].data, rd[c].data_size); (*res)->data_size = rd[c].data_size; (*res)->expiration_time = rd[c].expiration_time; (*res)->flags = rd[c].flags; (*res)->record_type = GNUNET_GNSRECORD_TYPE_NICK; return; } } (*res) = NULL; } /** * Return the NICK record for the zone (if it exists). * * @param zone private key for the zone to look for nick * @return NULL if no NICK record was found */ static struct GNUNET_GNSRECORD_Data * get_nick_record (const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone) { struct GNUNET_CRYPTO_EcdsaPublicKey pub; struct GNUNET_GNSRECORD_Data *nick; int res; nick = NULL; res = GSN_database->lookup_records (GSN_database->cls, zone, GNUNET_GNS_EMPTY_LABEL_AT, &lookup_nick_it, &nick); if ( (GNUNET_OK != res) || (NULL == nick) ) { GNUNET_CRYPTO_ecdsa_key_get_public (zone, &pub); GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, "No nick name set for zone `%s'\n", GNUNET_GNSRECORD_z2s (&pub)); return NULL; } return nick; } /** * Merge the nick record @a nick_rd with the rest of the * record set given in @a rd2. Store the result in @a rdc_res * and @a rd_res. The @a nick_rd's expiration time is set to * the maximum expiration time of all of the records in @a rd2. * * @param nick_rd the nick record to integrate * @param rd2_length length of the @a rd2 array * @param rd2 array of records * @param rdc_res[out] length of the resulting @a rd_res array * @param rd_res[out] set to an array of records, * including @a nick_rd and @a rd2; * all of the variable-size 'data' fields in @a rd2 are * allocated in the same chunk of memory! */ static void merge_with_nick_records (const struct GNUNET_GNSRECORD_Data *nick_rd, unsigned int rd2_length, const struct GNUNET_GNSRECORD_Data *rd2, unsigned int *rdc_res, struct GNUNET_GNSRECORD_Data **rd_res) { uint64_t latest_expiration; size_t req; char *data; int record_offset; size_t data_offset; (*rdc_res) = 1 + rd2_length; if (0 == 1 + rd2_length) { (*rd_res) = NULL; return; } req = 0; for (unsigned int c=0; c< 1; c++) req += sizeof (struct GNUNET_GNSRECORD_Data) + nick_rd[c].data_size; for (unsigned int c=0; c< rd2_length; c++) req += sizeof (struct GNUNET_GNSRECORD_Data) + rd2[c].data_size; (*rd_res) = GNUNET_malloc (req); data = (char *) &(*rd_res)[1 + rd2_length]; data_offset = 0; latest_expiration = 0; for (unsigned int c=0; c< rd2_length; c++) { if (0 != (rd2[c].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) { if ((GNUNET_TIME_absolute_get().abs_value_us + rd2[c].expiration_time) > latest_expiration) latest_expiration = rd2[c].expiration_time; } else if (rd2[c].expiration_time > latest_expiration) latest_expiration = rd2[c].expiration_time; (*rd_res)[c] = rd2[c]; (*rd_res)[c].data = (void *) &data[data_offset]; GNUNET_memcpy ((void *) (*rd_res)[c].data, rd2[c].data, rd2[c].data_size); data_offset += (*rd_res)[c].data_size; } /* append nick */ record_offset = rd2_length; (*rd_res)[record_offset] = *nick_rd; (*rd_res)[record_offset].expiration_time = latest_expiration; (*rd_res)[record_offset].data = (void *) &data[data_offset]; GNUNET_memcpy ((void *) (*rd_res)[record_offset].data, nick_rd->data, nick_rd->data_size); data_offset += (*rd_res)[record_offset].data_size; GNUNET_assert (req == (sizeof (struct GNUNET_GNSRECORD_Data)) * (*rdc_res) + data_offset); } /** * Generate a `struct LookupNameResponseMessage` and send it to the * given client using the given notification context. * * @param nc client to unicast to * @param request_id request ID to use * @param zone_key zone key of the zone * @param name name * @param rd_count number of records in @a rd * @param rd array of records */ static void send_lookup_response (struct NamestoreClient *nc, uint32_t request_id, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct GNUNET_MQ_Envelope *env; struct RecordResultMessage *zir_msg; struct GNUNET_GNSRECORD_Data *nick; struct GNUNET_GNSRECORD_Data *res; unsigned int res_count; size_t name_len; size_t rd_ser_len; char *name_tmp; char *rd_ser; nick = get_nick_record (zone_key); if ( (NULL != nick) && (0 != strcmp (name, GNUNET_GNS_EMPTY_LABEL_AT))) { nick->flags = (nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) ^ GNUNET_GNSRECORD_RF_PRIVATE; merge_with_nick_records (nick, rd_count, rd, &res_count, &res); GNUNET_free (nick); } else { res_count = rd_count; res = (struct GNUNET_GNSRECORD_Data *) rd; } name_len = strlen (name) + 1; rd_ser_len = GNUNET_GNSRECORD_records_get_size (res_count, res); env = GNUNET_MQ_msg_extra (zir_msg, name_len + rd_ser_len, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT); zir_msg->gns_header.r_id = htonl (request_id); zir_msg->name_len = htons (name_len); zir_msg->rd_count = htons (res_count); zir_msg->rd_len = htons (rd_ser_len); zir_msg->private_key = *zone_key; name_tmp = (char *) &zir_msg[1]; GNUNET_memcpy (name_tmp, name, name_len); rd_ser = &name_tmp[name_len]; GNUNET_GNSRECORD_records_serialize (res_count, res, rd_ser_len, rd_ser); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RECORD_RESULT message with %u records\n", res_count); GNUNET_STATISTICS_update (statistics, "Record sets sent to clients", 1, GNUNET_NO); GNUNET_MQ_send (nc->mq, env); if (rd != res) GNUNET_free (res); } /** * Send response to the store request to the client. * * @param client client to talk to * @param res status of the operation * @param rid client's request ID */ static void send_store_response (struct NamestoreClient *nc, int res, uint32_t rid) { struct GNUNET_MQ_Envelope *env; struct RecordStoreResponseMessage *rcr_msg; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RECORD_STORE_RESPONSE message\n"); env = GNUNET_MQ_msg (rcr_msg, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE); rcr_msg->gns_header.r_id = htonl (rid); rcr_msg->op_result = htonl (res); GNUNET_MQ_send (nc->mq, env); } /** * Cache operation complete, clean up. * * @param cls the `struct CacheOperation` * @param success success * @param emsg error messages */ static void finish_cache_operation (void *cls, int32_t success, const char *emsg) { struct CacheOperation *cop = cls; if (NULL != emsg) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to replicate block in namecache: %s\n"), emsg); else GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CACHE operation completed\n"); GNUNET_CONTAINER_DLL_remove (cop_head, cop_tail, cop); if (NULL != cop->nc) send_store_response (cop->nc, success, cop->rid); GNUNET_free (cop); } /** * We just touched the plaintext information about a name in our zone; * refresh the corresponding (encrypted) block in the namecache. * * @param nc client responsible for the request, can be NULL * @param rid request ID of the client * @param zone_key private key of the zone * @param name label for the records * @param rd_count number of records * @param rd records stored under the given @a name */ static void refresh_block (struct NamestoreClient *nc, uint32_t rid, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct GNUNET_GNSRECORD_Block *block; struct CacheOperation *cop; struct GNUNET_CRYPTO_EcdsaPublicKey pkey; struct GNUNET_GNSRECORD_Data *nick; struct GNUNET_GNSRECORD_Data *res; unsigned int res_count; struct GNUNET_TIME_Absolute exp_time; nick = get_nick_record (zone_key); res_count = rd_count; res = (struct GNUNET_GNSRECORD_Data *) rd; /* fixme: a bit unclean... */ if (NULL != nick) { nick->flags = (nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) ^ GNUNET_GNSRECORD_RF_PRIVATE; merge_with_nick_records (nick, rd_count,rd, &res_count, &res); GNUNET_free (nick); } exp_time = (0 == res_count) ? GNUNET_TIME_UNIT_ZERO_ABS : GNUNET_GNSRECORD_record_get_expiration_time (res_count, res); block = GNUNET_GNSRECORD_block_create (zone_key, exp_time, name, res, res_count); GNUNET_assert (NULL != block); GNUNET_CRYPTO_ecdsa_key_get_public (zone_key, &pkey); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Caching block for label `%s' with %u records and expiration %s in zone `%s' in namecache\n", name, res_count, GNUNET_STRINGS_absolute_time_to_string (exp_time), GNUNET_GNSRECORD_z2s (&pkey)); GNUNET_STATISTICS_update (statistics, "Namecache updates pushed", 1, GNUNET_NO); cop = GNUNET_new (struct CacheOperation); cop->nc = nc; cop->rid = rid; GNUNET_CONTAINER_DLL_insert (cop_head, cop_tail, cop); cop->qe = GNUNET_NAMECACHE_block_cache (namecache, block, &finish_cache_operation, cop); GNUNET_free (block); } /** * Closure for #lookup_it(). */ struct RecordLookupContext { /** * FIXME. */ const char *label; /** * FIXME. */ char *res_rd; /** * FIXME. */ struct GNUNET_GNSRECORD_Data *nick; /** * FIXME. */ int found; /** * FIXME. */ unsigned int res_rd_count; /** * FIXME. */ size_t rd_ser_len; }; /** * FIXME. */ static void lookup_it (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct RecordLookupContext *rlc = cls; struct GNUNET_GNSRECORD_Data *rd_res; unsigned int rdc_res; (void) private_key; if (0 == strcmp (label, rlc->label)) { rlc->found = GNUNET_YES; if (0 != rd_count) { if ( (NULL != rlc->nick) && (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT)) ) { /* Merge */ rd_res = NULL; rdc_res = 0; rlc->nick->flags = (rlc->nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) ^ GNUNET_GNSRECORD_RF_PRIVATE; merge_with_nick_records (rlc->nick, rd_count, rd, &rdc_res, &rd_res); rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rdc_res, rd_res); rlc->res_rd_count = rdc_res; rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); GNUNET_GNSRECORD_records_serialize (rdc_res, rd_res, rlc->rd_ser_len, rlc->res_rd); GNUNET_free (rd_res); GNUNET_free (rlc->nick); rlc->nick = NULL; } else { rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); rlc->res_rd_count = rd_count; rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); GNUNET_GNSRECORD_records_serialize (rd_count, rd, rlc->rd_ser_len, rlc->res_rd); } } else { rlc->rd_ser_len = 0; rlc->res_rd_count = 0; rlc->res_rd = NULL; } } } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message * * @param cls client sending the message * @param ll_msg message of type `struct LabelLookupMessage` * @return #GNUNET_OK if @a ll_msg is well-formed */ static int check_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) { uint32_t name_len; size_t src_size; const char *name_tmp; (void) cls; name_len = ntohl (ll_msg->label_len); src_size = ntohs (ll_msg->gns_header.header.size); if (name_len != src_size - sizeof (struct LabelLookupMessage)) { GNUNET_break (0); return GNUNET_SYSERR; } name_tmp = (const char *) &ll_msg[1]; if ('\0' != name_tmp[name_len -1]) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message * * @param cls client sending the message * @param ll_msg message of type `struct LabelLookupMessage` */ static void handle_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) { struct NamestoreClient *nc = cls; struct GNUNET_MQ_Envelope *env; struct LabelLookupResponseMessage *llr_msg; struct RecordLookupContext rlc; const char *name_tmp; char *res_name; char *conv_name; uint32_t name_len; int res; name_len = ntohl (ll_msg->label_len); name_tmp = (const char *) &ll_msg[1]; GNUNET_SERVICE_client_continue (nc->client); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received NAMESTORE_RECORD_LOOKUP message for name `%s'\n", name_tmp); conv_name = GNUNET_GNSRECORD_string_to_lowercase (name_tmp); if (NULL == conv_name) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error converting name `%s'\n", name_tmp); GNUNET_SERVICE_client_drop (nc->client); return; } rlc.label = conv_name; rlc.found = GNUNET_NO; rlc.res_rd_count = 0; rlc.res_rd = NULL; rlc.rd_ser_len = 0; rlc.nick = get_nick_record (&ll_msg->zone); res = GSN_database->lookup_records (GSN_database->cls, &ll_msg->zone, conv_name, &lookup_it, &rlc); GNUNET_free (conv_name); env = GNUNET_MQ_msg_extra (llr_msg, name_len + rlc.rd_ser_len, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE); llr_msg->gns_header.r_id = ll_msg->gns_header.r_id; llr_msg->private_key = ll_msg->zone; llr_msg->name_len = htons (name_len); llr_msg->rd_count = htons (rlc.res_rd_count); llr_msg->rd_len = htons (rlc.rd_ser_len); res_name = (char *) &llr_msg[1]; if ((GNUNET_YES == rlc.found) && (GNUNET_OK == res)) llr_msg->found = ntohs (GNUNET_YES); else llr_msg->found = ntohs (GNUNET_NO); GNUNET_memcpy (&llr_msg[1], name_tmp, name_len); GNUNET_memcpy (&res_name[name_len], rlc.res_rd, rlc.rd_ser_len); GNUNET_MQ_send (nc->mq, env); GNUNET_free_non_null (rlc.res_rd); } /** * Checks a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message * * @param cls client sending the message * @param rp_msg message of type `struct RecordStoreMessage` * @return #GNUNET_OK if @a rp_msg is well-formed */ static int check_record_store (void *cls, const struct RecordStoreMessage *rp_msg) { size_t name_len; size_t msg_size; size_t msg_size_exp; size_t rd_ser_len; const char *name_tmp; (void) cls; name_len = ntohs (rp_msg->name_len); msg_size = ntohs (rp_msg->gns_header.header.size); rd_ser_len = ntohs (rp_msg->rd_len); msg_size_exp = sizeof (struct RecordStoreMessage) + name_len + rd_ser_len; if (msg_size != msg_size_exp) { GNUNET_break (0); return GNUNET_SYSERR; } if ((0 == name_len) || (name_len > MAX_NAME_LEN)) { GNUNET_break (0); return GNUNET_SYSERR; } name_tmp = (const char *) &rp_msg[1]; if ('\0' != name_tmp[name_len -1]) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message * * @param cls client sending the message * @param rp_msg message of type `struct RecordStoreMessage` */ static void handle_record_store (void *cls, const struct RecordStoreMessage *rp_msg) { struct NamestoreClient *nc = cls; size_t name_len; size_t rd_ser_len; uint32_t rid; const char *name_tmp; char *conv_name; const char *rd_ser; unsigned int rd_count; int res; struct GNUNET_CRYPTO_EcdsaPublicKey pubkey; struct ZoneMonitor *zm; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received NAMESTORE_RECORD_STORE message\n"); rid = ntohl (rp_msg->gns_header.r_id); name_len = ntohs (rp_msg->name_len); rd_count = ntohs (rp_msg->rd_count); rd_ser_len = ntohs (rp_msg->rd_len); GNUNET_break (0 == ntohs (rp_msg->reserved)); name_tmp = (const char *) &rp_msg[1]; rd_ser = &name_tmp[name_len]; { struct GNUNET_GNSRECORD_Data rd[rd_count]; if (GNUNET_OK != GNUNET_GNSRECORD_records_deserialize (rd_ser_len, rd_ser, rd_count, rd)) { GNUNET_break (0); GNUNET_SERVICE_client_drop (nc->client); return; } /* Extracting and converting private key */ GNUNET_CRYPTO_ecdsa_key_get_public (&rp_msg->private_key, &pubkey); conv_name = GNUNET_GNSRECORD_string_to_lowercase (name_tmp); if (NULL == conv_name) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error converting name `%s'\n", name_tmp); GNUNET_SERVICE_client_drop (nc->client); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating %u records for name `%s' in zone `%s'\n", (unsigned int) rd_count, conv_name, GNUNET_GNSRECORD_z2s (&pubkey)); if ( (0 == rd_count) && (GNUNET_NO == GSN_database->iterate_records (GSN_database->cls, &rp_msg->private_key, 0, 1, NULL, 0)) ) { /* This name does not exist, so cannot be removed */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Name `%s' does not exist, no deletion required\n", conv_name); res = GNUNET_NO; } else { struct GNUNET_GNSRECORD_Data rd_clean[rd_count]; unsigned int rd_clean_off; /* remove "NICK" records, unless this is for the #GNUNET_GNS_EMPTY_LABEL_AT label */ rd_clean_off = 0; for (unsigned int i=0;istore_records (GSN_database->cls, &rp_msg->private_key, conv_name, rd_clean_off, rd_clean); if (GNUNET_OK == res) { for (zm = monitor_head; NULL != zm; zm = zm->next) { if ( (0 == memcmp (&rp_msg->private_key, &zm->zone, sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) || (0 == memcmp (&zm->zone, &zero, sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Notifying monitor about changes under label `%s'\n", conv_name); send_lookup_response (zm->nc, 0, &rp_msg->private_key, conv_name, rd_count, rd); } else GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Monitor is for another zone\n"); } if (NULL == monitor_head) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No monitors active\n"); } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error storing record: %d\n", res); } } if (GNUNET_OK == res) { refresh_block (nc, rid, &rp_msg->private_key, conv_name, rd_count, rd); GNUNET_SERVICE_client_continue (nc->client); GNUNET_free (conv_name); return; } GNUNET_free (conv_name); } send_store_response (nc, res, rid); GNUNET_SERVICE_client_continue (nc->client); } /** * Context for record remove operations passed from #handle_zone_to_name to * #handle_zone_to_name_it as closure */ struct ZoneToNameCtx { /** * Namestore client */ struct NamestoreClient *nc; /** * Request id (to be used in the response to the client). */ uint32_t rid; /** * Set to #GNUNET_OK on success, #GNUNET_SYSERR on error. Note that * not finding a name for the zone still counts as a 'success' here, * as this field is about the success of executing the IPC protocol. */ int success; }; /** * Zone to name iterator * * @param cls struct ZoneToNameCtx * * @param zone_key the zone key * @param name name * @param rd_count number of records in @a rd * @param rd record data */ static void handle_zone_to_name_it (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct ZoneToNameCtx *ztn_ctx = cls; struct GNUNET_MQ_Envelope *env; struct ZoneToNameResponseMessage *ztnr_msg; int16_t res; size_t name_len; size_t rd_ser_len; size_t msg_size; char *name_tmp; char *rd_tmp; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found result for zone-to-name lookup: `%s'\n", name); res = GNUNET_YES; name_len = (NULL == name) ? 0 : strlen (name) + 1; rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); msg_size = sizeof (struct ZoneToNameResponseMessage) + name_len + rd_ser_len; if (msg_size >= GNUNET_MAX_MESSAGE_SIZE) { GNUNET_break (0); ztn_ctx->success = GNUNET_SYSERR; return; } env = GNUNET_MQ_msg_extra (ztnr_msg, name_len + rd_ser_len, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); ztnr_msg->gns_header.header.size = htons (msg_size); ztnr_msg->gns_header.r_id = htonl (ztn_ctx->rid); ztnr_msg->res = htons (res); ztnr_msg->rd_len = htons (rd_ser_len); ztnr_msg->rd_count = htons (rd_count); ztnr_msg->name_len = htons (name_len); ztnr_msg->zone = *zone_key; name_tmp = (char *) &ztnr_msg[1]; GNUNET_memcpy (name_tmp, name, name_len); rd_tmp = &name_tmp[name_len]; GNUNET_GNSRECORD_records_serialize (rd_count, rd, rd_ser_len, rd_tmp); ztn_ctx->success = GNUNET_OK; GNUNET_MQ_send (ztn_ctx->nc->mq, env); } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME message * * @param cls client client sending the message * @param ztn_msg message of type 'struct ZoneToNameMessage' */ static void handle_zone_to_name (void *cls, const struct ZoneToNameMessage *ztn_msg) { struct NamestoreClient *nc = cls; struct ZoneToNameCtx ztn_ctx; struct GNUNET_MQ_Envelope *env; struct ZoneToNameResponseMessage *ztnr_msg; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "ZONE_TO_NAME"); ztn_ctx.rid = ntohl (ztn_msg->gns_header.r_id); ztn_ctx.nc = nc; ztn_ctx.success = GNUNET_NO; if (GNUNET_SYSERR == GSN_database->zone_to_name (GSN_database->cls, &ztn_msg->zone, &ztn_msg->value_zone, &handle_zone_to_name_it, &ztn_ctx)) { /* internal error, hang up instead of signalling something that might be wrong */ GNUNET_break (0); GNUNET_SERVICE_client_drop (nc->client); return; } if (GNUNET_NO == ztn_ctx.success) { /* no result found, send empty response */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found no result for zone-to-name lookup.\n"); env = GNUNET_MQ_msg (ztnr_msg, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); ztnr_msg->gns_header.r_id = ztn_msg->gns_header.r_id; ztnr_msg->res = htons (GNUNET_NO); GNUNET_MQ_send (nc->mq, env); } GNUNET_SERVICE_client_continue (nc->client); } /** * Zone iteration processor result */ enum ZoneIterationResult { /** * Iteration start. */ IT_START = 0, /** * Found records, * Continue to iterate with next iteration_next call */ IT_SUCCESS_MORE_AVAILABLE = 1, /** * Iteration complete */ IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE = 2 }; /** * Context for record remove operations passed from * #run_zone_iteration_round to #zone_iterate_proc as closure */ struct ZoneIterationProcResult { /** * The zone iteration handle */ struct ZoneIteration *zi; /** * Number of results left to be returned in this iteration. */ uint64_t limit; /** * Iteration result: iteration done? * #IT_SUCCESS_MORE_AVAILABLE: if there may be more results overall but * we got one for now and have sent it to the client * #IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE: if there are no further results, * #IT_START: if we are still trying to find a result. */ int res_iteration_finished; }; /** * Process results for zone iteration from database * * @param cls struct ZoneIterationProcResult *proc * @param zone_key the zone key * @param name name * @param rd_count number of records for this name * @param rd record data */ static void zone_iterate_proc (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct ZoneIterationProcResult *proc = cls; int do_refresh_block; if ( (NULL == zone_key) && (NULL == name) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration done\n"); proc->res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE; return; } if ( (NULL == zone_key) || (NULL == name) ) { /* what is this!? should never happen */ proc->res_iteration_finished = IT_START; GNUNET_break (0); return; } GNUNET_assert (proc->limit > 0); proc->limit--; if (0 == proc->limit) proc->res_iteration_finished = IT_SUCCESS_MORE_AVAILABLE; send_lookup_response (proc->zi->nc, proc->zi->request_id, zone_key, name, rd_count, rd); do_refresh_block = GNUNET_NO; for (unsigned int i=0;iiterate_records (GSN_database->cls, (0 == memcmp (&zi->zone, &zero, sizeof (zero))) ? NULL : &zi->zone, zi->offset, limit, &zone_iterate_proc, &proc))) { GNUNET_break (0); break; } if (GNUNET_NO == ret) proc.res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE; zi->offset++; } if (IT_SUCCESS_MORE_AVAILABLE == proc.res_iteration_finished) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "More results available\n"); return; /* more results later after we get the #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT message */ } /* send empty response to indicate end of list */ env = GNUNET_MQ_msg (rrm, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT); rrm->gns_header.r_id = htonl (zi->request_id); GNUNET_MQ_send (zi->nc->mq, env); GNUNET_CONTAINER_DLL_remove (zi->nc->op_head, zi->nc->op_tail, zi); GNUNET_free (zi); } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START message * * @param cls the client sending the message * @param zis_msg message from the client */ static void handle_iteration_start (void *cls, const struct ZoneIterationStartMessage *zis_msg) { struct NamestoreClient *nc = cls; struct ZoneIteration *zi; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_ITERATION_START message\n"); zi = GNUNET_new (struct ZoneIteration); zi->request_id = ntohl (zis_msg->gns_header.r_id); zi->offset = 0; zi->nc = nc; zi->zone = zis_msg->zone; GNUNET_CONTAINER_DLL_insert (nc->op_head, nc->op_tail, zi); run_zone_iteration_round (zi, 1); GNUNET_SERVICE_client_continue (nc->client); } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP message * * @param cls the client sending the message * @param zis_msg message from the client */ static void handle_iteration_stop (void *cls, const struct ZoneIterationStopMessage *zis_msg) { struct NamestoreClient *nc = cls; struct ZoneIteration *zi; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "ZONE_ITERATION_STOP"); rid = ntohl (zis_msg->gns_header.r_id); for (zi = nc->op_head; NULL != zi; zi = zi->next) if (zi->request_id == rid) break; if (NULL == zi) { GNUNET_break (0); GNUNET_SERVICE_client_drop (nc->client); return; } GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, zi); GNUNET_free (zi); GNUNET_SERVICE_client_continue (nc->client); } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT message * * @param cls the client sending the message * @param message message from the client */ static void handle_iteration_next (void *cls, const struct ZoneIterationNextMessage *zis_msg) { struct NamestoreClient *nc = cls; struct ZoneIteration *zi; uint32_t rid; uint64_t limit; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_ITERATION_NEXT message\n"); GNUNET_STATISTICS_update (statistics, "Iteration NEXT messages received", 1, GNUNET_NO); rid = ntohl (zis_msg->gns_header.r_id); limit = GNUNET_ntohll (zis_msg->limit); for (zi = nc->op_head; NULL != zi; zi = zi->next) if (zi->request_id == rid) break; if (NULL == zi) { GNUNET_break (0); GNUNET_SERVICE_client_drop (nc->client); return; } run_zone_iteration_round (zi, limit); GNUNET_SERVICE_client_continue (nc->client); } /** * Send 'sync' message to zone monitor, we're now in sync. * * @param zm monitor that is now in sync */ static void monitor_sync (struct ZoneMonitor *zm) { struct GNUNET_MQ_Envelope *env; struct GNUNET_MessageHeader *sync; env = GNUNET_MQ_msg (sync, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_SYNC); GNUNET_MQ_send (zm->nc->mq, env); } /** * Obtain the next datum during the zone monitor's zone intiial iteration. * * @param cls zone monitor that does its initial iteration */ static void monitor_next (void *cls); /** * A #GNUNET_NAMESTORE_RecordIterator for monitors. * * @param cls a 'struct ZoneMonitor *' with information about the monitor * @param zone_key zone key of the zone * @param name name * @param rd_count number of records in @a rd * @param rd array of records */ static void monitor_iterate_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *name, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct ZoneMonitor *zm = cls; if (NULL == name) { /* finished with iteration */ monitor_sync (zm); return; } GNUNET_STATISTICS_update (statistics, "Monitor notifications sent", 1, GNUNET_NO); send_lookup_response (zm->nc, 0, zone_key, name, rd_count, rd); zm->task = GNUNET_SCHEDULER_add_now (&monitor_next, zm); } /** * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START message * * @param cls the client sending the message * @param message message from the client */ static void handle_monitor_start (void *cls, const struct ZoneMonitorStartMessage *zis_msg) { struct NamestoreClient *nc = cls; struct ZoneMonitor *zm; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_MONITOR_START message\n"); zm = GNUNET_new (struct ZoneMonitor); zm->nc = nc; zm->zone = zis_msg->zone; GNUNET_CONTAINER_DLL_insert (monitor_head, monitor_tail, zm); GNUNET_SERVICE_client_mark_monitor (nc->client); GNUNET_SERVICE_client_disable_continue_warning (nc->client); GNUNET_notification_context_add (monitor_nc, nc->mq); if (GNUNET_YES == ntohl (zis_msg->iterate_first)) zm->task = GNUNET_SCHEDULER_add_now (&monitor_next, zm); else monitor_sync (zm); } /** * Obtain the next datum during the zone monitor's zone intiial iteration. * * @param cls zone monitor that does its initial iteration */ static void monitor_next (void *cls) { struct ZoneMonitor *zm = cls; int ret; zm->task = NULL; ret = GSN_database->iterate_records (GSN_database->cls, (0 == memcmp (&zm->zone, &zero, sizeof (zero))) ? NULL : &zm->zone, zm->offset++, 1, &monitor_iterate_cb, zm); if (GNUNET_SYSERR == ret) { GNUNET_SERVICE_client_drop (zm->nc->client); return; } if (GNUNET_NO == ret) { /* empty zone */ monitor_sync (zm); return; } } /** * Process namestore requests. * * @param cls closure * @param cfg configuration to use * @param service the initialized service */ static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_SERVICE_Handle *service) { char *database; (void) cls; (void) service; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting namestore service\n"); GSN_cfg = cfg; monitor_nc = GNUNET_notification_context_create (1); namecache = GNUNET_NAMECACHE_connect (cfg); /* Loading database plugin */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "namestore", "database", &database)) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_namestore_%s", database); GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) GSN_cfg); GNUNET_free (database); statistics = GNUNET_STATISTICS_create ("namestore", cfg); GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); if (NULL == GSN_database) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not load database backend `%s'\n", db_lib_name); GNUNET_SCHEDULER_shutdown (); return; } } /** * Define "main" method using service macro. */ GNUNET_SERVICE_MAIN ("namestore", GNUNET_SERVICE_OPTION_NONE, &run, &client_connect_cb, &client_disconnect_cb, NULL, GNUNET_MQ_hd_var_size (record_store, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE, struct RecordStoreMessage, NULL), GNUNET_MQ_hd_var_size (record_lookup, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP, struct LabelLookupMessage, NULL), GNUNET_MQ_hd_fixed_size (zone_to_name, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME, struct ZoneToNameMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_start, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START, struct ZoneIterationStartMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_next, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT, struct ZoneIterationNextMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_stop, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP, struct ZoneIterationStopMessage, NULL), GNUNET_MQ_hd_fixed_size (monitor_start, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START, struct ZoneMonitorStartMessage, NULL), GNUNET_MQ_handler_end ()); /* end of gnunet-service-namestore.c */