/** * Iterator for local get request results, * * @param cls closure for iterator, a DatacacheGetContext * @param exp when does this value expire? * @param key the key this data is stored under * @param size the size of the data identified by key * @param data the actual data * @param type the type of the data * @param put_path_length number of peers in 'put_path' * @param put_path path the reply took on put * @return GNUNET_OK to continue iteration, anything else * to stop iteration. */ static int datacache_get_iterator (void *cls, const struct GNUNET_HashCode * key, size_t size, const char *data, enum GNUNET_BLOCK_Type type, struct GNUNET_TIME_Absolute exp, unsigned int put_path_length, const struct GNUNET_PeerIdentity *put_path) { struct GetRequestContext *ctx = cls; enum GNUNET_BLOCK_EvaluationResult eval; eval = GNUNET_BLOCK_evaluate (GDS_block_context, type, key, ctx->reply_bf, ctx->reply_bf_mutator, ctx->xquery, ctx->xquery_size, data, size); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found reply for query %s in datacache, evaluation result is %d\n", GNUNET_h2s (key), (int) eval); ctx->eval = eval; switch (eval) { case GNUNET_BLOCK_EVALUATION_OK_LAST: case GNUNET_BLOCK_EVALUATION_OK_MORE: /* forward to local clients */ GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Good RESULTS found in datacache"), 1, GNUNET_NO); GDS_CLIENTS_handle_reply (exp, key, 0, NULL, put_path_length, put_path, type, size, data); /* forward to other peers */ GDS_ROUTING_process (type, exp, key, put_path_length, put_path, 0, NULL, data, size); break; case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Duplicate RESULTS found in datacache"), 1, GNUNET_NO); break; case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Invalid RESULTS found in datacache"), 1, GNUNET_NO); break; case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Irrelevant RESULTS found in datacache"), 1, GNUNET_NO); break; case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: GNUNET_break (0); break; case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: GNUNET_break_op (0); return GNUNET_SYSERR; case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Unsupported RESULTS found in datacache"), 1, GNUNET_NO); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Unsupported block type (%u) in local response!\n"), type); break; } return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK; }
/** * Iterator over hash map entries that send a given reply to * each of the matching clients. With some tricky recycling * of the buffer. * * @param cls the 'struct ForwardReplyContext' * @param key current key * @param value value in the hash map, a ClientQueryRecord * @return GNUNET_YES (we should continue to iterate), * if the result is mal-formed, GNUNET_NO */ static int forward_reply (void *cls, const struct GNUNET_HashCode * key, void *value) { struct ForwardReplyContext *frc = cls; struct ClientQueryRecord *record = value; struct PendingMessage *pm; struct GNUNET_DHT_ClientResultMessage *reply; enum GNUNET_BLOCK_EvaluationResult eval; int do_free; struct GNUNET_HashCode ch; unsigned int i; LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG, "XDHT CLIENT-RESULT %s\n", GNUNET_h2s (key)); if ((record->type != GNUNET_BLOCK_TYPE_ANY) && (record->type != frc->type)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Record type missmatch, not passing request for key %s to local client\n", GNUNET_h2s (key)); GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Key match, type mismatches in REPLY to CLIENT"), 1, GNUNET_NO); return GNUNET_YES; /* type mismatch */ } GNUNET_CRYPTO_hash (frc->data, frc->data_size, &ch); for (i = 0; i < record->seen_replies_count; i++) if (0 == memcmp (&record->seen_replies[i], &ch, sizeof (struct GNUNET_HashCode))) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Duplicate reply, not passing request for key %s to local client\n", GNUNET_h2s (key)); GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Duplicate REPLIES to CLIENT request dropped"), 1, GNUNET_NO); return GNUNET_YES; /* duplicate */ } eval = GNUNET_BLOCK_evaluate (GDS_block_context, record->type, key, NULL, 0, record->xquery, record->xquery_size, frc->data, frc->data_size); LOG (GNUNET_ERROR_TYPE_DEBUG, "Evaluation result is %d for key %s for local client's query\n", (int) eval, GNUNET_h2s (key)); switch (eval) { case GNUNET_BLOCK_EVALUATION_OK_LAST: do_free = GNUNET_YES; break; case GNUNET_BLOCK_EVALUATION_OK_MORE: GNUNET_array_append (record->seen_replies, record->seen_replies_count, ch); do_free = GNUNET_NO; break; case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: /* should be impossible to encounter here */ GNUNET_break (0); return GNUNET_YES; case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: GNUNET_break_op (0); return GNUNET_NO; case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: GNUNET_break (0); return GNUNET_NO; case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: GNUNET_break (0); return GNUNET_NO; case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT: return GNUNET_YES; case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Unsupported block type (%u) in request!\n"), record->type); return GNUNET_NO; default: GNUNET_break (0); return GNUNET_NO; } if (GNUNET_NO == frc->do_copy) { /* first time, we can use the original data */ pm = frc->pm; frc->do_copy = GNUNET_YES; } else { /* two clients waiting for same reply, must copy for queueing */ pm = GNUNET_malloc (sizeof (struct PendingMessage) + ntohs (frc->pm->msg->size)); memcpy (pm, frc->pm, sizeof (struct PendingMessage) + ntohs (frc->pm->msg->size)); pm->next = pm->prev = NULL; pm->msg = (struct GNUNET_MessageHeader *) &pm[1]; } GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# RESULTS queued for clients"), 1, GNUNET_NO); reply = (struct GNUNET_DHT_ClientResultMessage *) &pm[1]; reply->unique_id = record->unique_id; LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing reply to query %s for client %p\n", GNUNET_h2s (key), record->client->client_handle); add_pending_message (record->client, pm); if (GNUNET_YES == do_free) remove_client_records (record->client, key, record); return GNUNET_YES; }
/** * Forward the result to the given peer if it matches the request. * * @param cls the 'struct ProcessContext' with the result * @param key the query * @param value the 'struct RecentRequest' with the request * @return GNUNET_OK (continue to iterate), * GNUNET_SYSERR if the result is malformed or type unsupported */ static int process (void *cls, const struct GNUNET_HashCode * key, void *value) { struct ProcessContext *pc = cls; struct RecentRequest *rr = value; enum GNUNET_BLOCK_EvaluationResult eval; unsigned int gpl; unsigned int ppl; struct GNUNET_HashCode hc; const struct GNUNET_HashCode *eval_key; if ((rr->type != GNUNET_BLOCK_TYPE_ANY) && (rr->type != pc->type)) return GNUNET_OK; /* type missmatch */ if (0 != (rr->options & GNUNET_DHT_RO_RECORD_ROUTE)) { gpl = pc->get_path_length; ppl = pc->put_path_length; } else { gpl = 0; ppl = 0; } if ((0 != (rr->options & GNUNET_DHT_RO_FIND_PEER)) && (pc->type == GNUNET_BLOCK_TYPE_DHT_HELLO)) { /* key may not match HELLO, which is OK since * the search is approximate. Still, the evaluation * would fail since the match is not exact. So * we fake it by changing the key to the actual PID ... */ GNUNET_BLOCK_get_key (GDS_block_context, GNUNET_BLOCK_TYPE_DHT_HELLO, pc->data, pc->data_size, &hc); eval_key = &hc; } else { eval_key = key; } eval = GNUNET_BLOCK_evaluate (GDS_block_context, pc->type, eval_key, &rr->reply_bf, rr->reply_bf_mutator, rr->xquery, rr->xquery_size, pc->data, pc->data_size); switch (eval) { case GNUNET_BLOCK_EVALUATION_OK_MORE: case GNUNET_BLOCK_EVALUATION_OK_LAST: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Good REPLIES matched against routing table"), 1, GNUNET_NO); GDS_NEIGHBOURS_handle_reply (&rr->peer, pc->type, pc->expiration_time, key, ppl, pc->put_path, gpl, pc->get_path, pc->data, pc->data_size); break; case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Duplicate REPLIES matched against routing table"), 1, GNUNET_NO); return GNUNET_OK; case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Invalid REPLIES matched against routing table"), 1, GNUNET_NO); return GNUNET_SYSERR; case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Irrelevant REPLIES matched against routing table"), 1, GNUNET_NO); return GNUNET_OK; case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: GNUNET_break (0); return GNUNET_OK; case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: GNUNET_break (0); return GNUNET_OK; case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Unsupported REPLIES matched against routing table"), 1, GNUNET_NO); return GNUNET_SYSERR; default: GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; }