+ gph->namestore_task = NULL;
+ /* no pseu found */
+ if (rd_count == 0)
+ {
+ /**
+ * check dht
+ */
+ GNUNET_CRYPTO_short_hash ("+", strlen ("+"), &name_hash);
+ GNUNET_CRYPTO_short_hash_double (&name_hash, &name_hash_double);
+ GNUNET_CRYPTO_short_hash_double (&gph->ahead->zone, &zone_hash_double);
+ GNUNET_CRYPTO_hash_xor (&name_hash_double, &zone_hash_double, &lookup_key);
+ GNUNET_CRYPTO_hash_to_enc (&lookup_key, &lookup_key_string);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_AUTO_PSEU: starting dht lookup for %s with key: %s\n",
+ "+", (char*)&lookup_key_string);
+
+ gph->timeout = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
+ &handle_auth_discovery_timeout, gph);
+
+ xquery = htonl (GNUNET_GNS_RECORD_PSEU);
+
+ GNUNET_assert (gph->get_handle == NULL);
+
+ gph->get_handle = GNUNET_DHT_get_start(dht_handle,
+ GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
+ &lookup_key,
+ DHT_GNS_REPLICATION_LEVEL,
+ GNUNET_DHT_RO_NONE,
+ &xquery,
+ sizeof(xquery),
+ &process_auth_discovery_dht_result,
+ gph);
+ return;
+ }
+
+ for (i=0; i < rd_count; i++)
+ {
+ if ((strcmp (name, "+") == 0) &&
+ (rd[i].record_type == GNUNET_GNS_RECORD_PSEU))
+ {
+ /* found pseu */
+ process_pseu_result (gph, (char*)rd[i].data);
+ return;
+ }
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNS_GET_AUTH: no pseu in namestore!\n");
+
+ if (gph->ahead->next != NULL)
+ {
+ if (GNUNET_CRYPTO_short_hash_cmp (&gph->ahead->next->zone,
+ &gph->our_zone))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNS_GET_AUTH: trying next!\n");
+ iter = gph->ahead->next;
+ GNUNET_free (gph->ahead);
+ gph->ahead = iter;
+ shorten_authority_chain (gph);
+ return;
+ }
+ }
+
+ process_pseu_result (gph, NULL);
+}
+
+/**
+ * Callback called by namestore for a zone to name
+ * result
+ *
+ * @param cls the closure
+ * @param zone_key the zone we queried
+ * @param expire the expiration time of the name
+ * @param name the name found or NULL
+ * @param rd_len number of records for the name
+ * @param rd the record data (PKEY) for the name
+ * @param signature the signature for the record data
+ */
+static void
+process_zone_to_name_discover (void *cls,
+ const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
+ struct GNUNET_TIME_Absolute expire,
+ const char *name,
+ unsigned int rd_len,
+ const struct GNUNET_NAMESTORE_RecordData *rd,
+ const struct GNUNET_CRYPTO_RsaSignature *signature)
+{
+ struct GetPseuAuthorityHandle* gph = (struct GetPseuAuthorityHandle*)cls;
+ struct AuthorityChain *iter;
+
+ gph->namestore_task = NULL;
+ /* we found a match in our own zone */
+ if (rd_len != 0)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_AUTO_PSEU: name for zone in our root %s\n", name);
+
+ iter = gph->ahead;
+ do
+ {
+ iter = gph->ahead->next;
+ GNUNET_free (gph->ahead);
+ gph->ahead = iter;
+ } while (iter != NULL);
+ GNUNET_CRYPTO_rsa_key_free (gph->key);
+ GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, gph);
+ GNUNET_free (gph);
+ }
+ else
+ {
+
+ gph->namestore_task = GNUNET_NAMESTORE_lookup_record (namestore_handle,
+ &gph->ahead->zone,
+ "+",
+ GNUNET_GNS_RECORD_PSEU,
+ &process_auth_discovery_ns_result,
+ gph);
+ }
+
+}
+
+
+/**
+ * Callback that shortens authorities
+ *
+ * @param gph the handle to the shorten request
+ */
+static void
+shorten_authority_chain (struct GetPseuAuthorityHandle *gph)
+{
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_AUTO_PSEU: New authority %s discovered\n",
+ gph->ahead->name);
+
+ gph->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
+ &gph->our_zone,
+ &gph->ahead->zone,
+ &process_zone_to_name_discover,
+ gph);
+
+}
+
+static void
+start_shorten (struct AuthorityChain *atail,
+ struct GNUNET_CRYPTO_RsaPrivateKey *key)
+{
+ struct AuthorityChain *new_head = NULL;
+ struct AuthorityChain *new_tail = NULL;
+ struct AuthorityChain *iter;
+ struct AuthorityChain *acopy;
+ struct GetPseuAuthorityHandle *gph;
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
+ struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *pb_key;
+
+ /* First copy the authority chain in reverse order */
+ for (iter = atail; iter != NULL; iter = iter->prev)
+ {
+ acopy = GNUNET_malloc (sizeof (struct AuthorityChain));
+ memcpy (acopy, iter, sizeof (struct AuthorityChain));
+ acopy->next = NULL;
+ acopy->prev = NULL;
+ GNUNET_CONTAINER_DLL_insert (new_head, new_tail, acopy);
+ }
+
+ gph = GNUNET_malloc (sizeof (struct GetPseuAuthorityHandle));
+
+ GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
+ pb_key = GNUNET_CRYPTO_rsa_encode_key (key);
+ gph->key = GNUNET_CRYPTO_rsa_decode_key ((char*)pb_key, ntohs (pb_key->len));
+ //gph->key = key;//GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
+ //memcpy (gph->key, key, sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
+
+ GNUNET_CRYPTO_short_hash (&pkey,
+ sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+ &gph->our_zone);
+ gph->ahead = new_head;
+
+ GNUNET_CONTAINER_DLL_insert (gph_head, gph_tail, gph);
+
+ shorten_authority_chain (gph);
+}
+
+/**
+ * Initialize the resolver
+ *
+ * @param nh the namestore handle
+ * @param dh the dht handle
+ * @param lz the local zone's hash
+ * @param c configuration handle
+ * @param max_bg_queries maximum number of parallel background queries in dht
+ * @param ignore_pending ignore records that still require user confirmation
+ * on lookup
+ * @return GNUNET_OK on success
+ */
+int
+gns_resolver_init (struct GNUNET_NAMESTORE_Handle *nh,
+ struct GNUNET_DHT_Handle *dh,
+ struct GNUNET_CRYPTO_ShortHashCode lz,
+ const struct GNUNET_CONFIGURATION_Handle *c,
+ unsigned long long max_bg_queries,
+ int ignore_pending)
+{
+ cfg = c;
+ namestore_handle = nh;
+ dht_handle = dh;
+ local_zone = lz;
+ dht_lookup_heap =
+ GNUNET_CONTAINER_heap_create(GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ ns_task_heap =
+ GNUNET_CONTAINER_heap_create(GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ max_allowed_background_queries = max_bg_queries;
+ max_allowed_ns_tasks = GNUNET_GNS_MAX_NS_TASKS;
+
+ ignore_pending_records = ignore_pending;
+
+ gph_head = NULL;
+ gph_tail = NULL;
+ rlh_head = NULL;
+ rlh_tail = NULL;
+ nsh_head = NULL;
+ nsh_tail = NULL;
+ nah_head = NULL;
+ nah_tail = NULL;
+
+ GNUNET_RESOLVER_connect (cfg);
+
+
+ if ((namestore_handle != NULL) && (dht_handle != NULL))
+ {
+ return GNUNET_OK;
+ }
+
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Cleanup ns tasks
+ *
+ * @param cls closure to iterator
+ * @param node heap nodes
+ * @param element the namestorebgtask
+ * @param cost heap cost
+ * @return always GNUNET_YES
+ */
+static int
+cleanup_pending_ns_tasks(void* cls,
+ struct GNUNET_CONTAINER_HeapNode *node,
+ void *element,
+ GNUNET_CONTAINER_HeapCostType cost)
+{
+ struct NamestoreBGTask *nbg = (struct NamestoreBGTask *)element;
+ ResolverCleanupContinuation cont = cls;
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_CLEANUP: Terminating ns task\n");
+ GNUNET_NAMESTORE_cancel (nbg->qe);
+
+ GNUNET_CONTAINER_heap_remove_node(node);
+
+ if (GNUNET_CONTAINER_heap_get_size(ns_task_heap) == 0)
+ cont();
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * finish lookup
+ *
+ * @param rh resolver handle
+ * @param rlh record lookup handle
+ * @param rd_count number of results
+ * @param rd results
+ */
+static void
+finish_lookup (struct ResolverHandle *rh,
+ struct RecordLookupHandle* rlh,
+ unsigned int rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd);
+
+
+/**
+ * Cleanup background lookups FIXME get rid of this??
+ *
+ * @param cls closure to iterator
+ * @param node heap nodes
+ * @param element the resolver handle
+ * @param cost heap cost
+ * @return always GNUNET_YES
+ */
+static int
+cleanup_pending_background_queries (void* cls,
+ struct GNUNET_CONTAINER_HeapNode *node,
+ void *element,
+ GNUNET_CONTAINER_HeapCostType cost)
+{
+ struct ResolverHandle *rh = (struct ResolverHandle *)element;
+ ResolverCleanupContinuation cont = cls;
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_CLEANUP-%llu: Terminating background lookup for %s\n",
+ rh->id, rh->name);
+
+ //finish_lookup (rh, rh->proc_cls, 0, NULL);
+ //rh->get_handle = NULL;
+ //rh->proc(rh->proc_cls, rh, 0, NULL);
+
+ GNUNET_CONTAINER_heap_remove_node(node);
+
+ if (GNUNET_CONTAINER_heap_get_size(dht_lookup_heap) == 0)
+ {
+ if (GNUNET_CONTAINER_heap_get_size(ns_task_heap) == 0)
+ cont();
+ else
+ {
+ GNUNET_CONTAINER_heap_iterate (ns_task_heap,
+ &cleanup_pending_ns_tasks,
+ cont);
+ }
+ }
+
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Helper function to free resolver handle
+ *
+ * @param rh the handle to free
+ */
+static void
+free_resolver_handle (struct ResolverHandle* rh)
+{
+ struct AuthorityChain *ac;
+ struct AuthorityChain *ac_next;
+
+ if (NULL == rh)
+ return;
+
+ ac = rh->authority_chain_head;
+
+ while (NULL != ac)
+ {
+ ac_next = ac->next;
+ GNUNET_free(ac);
+ ac = ac_next;
+ }
+
+ if (NULL != rh->get_handle)
+ GNUNET_DHT_get_stop (rh->get_handle);
+
+ if (NULL != rh->dns_raw_packet)
+ GNUNET_free (rh->dns_raw_packet);
+
+ if (NULL != rh->namestore_task)
+ GNUNET_NAMESTORE_cancel (rh->namestore_task);
+ rh->namestore_task = NULL;
+
+ if (GNUNET_SCHEDULER_NO_TASK != rh->dns_read_task)
+ GNUNET_SCHEDULER_cancel (rh->dns_read_task);
+
+ if (GNUNET_SCHEDULER_NO_TASK != rh->timeout_task)
+ GNUNET_SCHEDULER_cancel (rh->timeout_task);
+
+ if (NULL != rh->dns_sock)
+ GNUNET_NETWORK_socket_close (rh->dns_sock);
+ if (NULL != rh->dns_resolver_handle)
+ GNUNET_RESOLVER_request_cancel (rh->dns_resolver_handle);
+
+ if (NULL != rh->rd.data)
+ GNUNET_free ((void*)(rh->rd.data));
+ GNUNET_free(rh);
+}
+
+
+/**
+ * finish shorten
+ *
+ * @param rh resolver handle
+ * @param nsh name shorten handle
+ */
+static void
+finish_shorten (struct ResolverHandle *rh,
+ struct NameShortenHandle *nsh);
+/**
+ * finish get auth
+ *
+ * @param rh resolver handle
+ * @param nah get name authority handle
+ */
+static void
+finish_get_auth (struct ResolverHandle *rh,
+ struct GetNameAuthorityHandle* rlh);
+/**
+ * Shutdown resolver
+ */
+void
+gns_resolver_cleanup (ResolverCleanupContinuation cont)
+{
+ unsigned int s;
+ struct GetPseuAuthorityHandle *tmp;
+ struct AuthorityChain *iter;
+
+
+ tmp = gph_head;
+ for (tmp = gph_head; tmp != NULL; tmp = gph_head)
+ {
+ if (tmp->get_handle != NULL)
+ GNUNET_DHT_get_stop (tmp->get_handle);
+ tmp->get_handle = NULL;
+ if (tmp->timeout != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (tmp->timeout);
+ tmp->timeout = GNUNET_SCHEDULER_NO_TASK;
+
+ if (NULL != tmp->namestore_task)
+ GNUNET_NAMESTORE_cancel (tmp->namestore_task);
+ tmp->namestore_task = NULL;
+
+ iter = tmp->ahead;
+ do
+ {
+ iter = tmp->ahead->next;
+ GNUNET_free (tmp->ahead);
+ tmp->ahead = iter;
+ } while (iter != NULL);
+
+ GNUNET_CRYPTO_rsa_key_free (tmp->key);
+ GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, tmp);
+ GNUNET_free (tmp);
+ }
+
+ while (NULL != rlh_head)
+ {
+ finish_lookup (rlh_head, rlh_head->proc_cls, 0, NULL);
+ }
+
+ while (NULL != nsh_head)
+ {
+ finish_shorten (nsh_head, nsh_head->proc_cls);
+ }
+
+ while (NULL != nah_head)
+ {
+ finish_get_auth (nah_head, nah_head->proc_cls);
+ }
+
+ s = GNUNET_CONTAINER_heap_get_size(dht_lookup_heap);
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_CLEANUP: %d pending background queries to terminate\n", s);
+ if (0 != s)
+ GNUNET_CONTAINER_heap_iterate (dht_lookup_heap,
+ &cleanup_pending_background_queries,
+ cont);
+ else if (0 != GNUNET_CONTAINER_heap_get_size(ns_task_heap))
+ {
+ GNUNET_CONTAINER_heap_iterate (ns_task_heap,
+ &cleanup_pending_ns_tasks,
+ cont);
+ }
+ else
+ cont();
+}
+
+
+
+
+/**
+ * Callback when record data is put into namestore
+ *
+ * @param cls the closure
+ * @param success GNUNET_OK on success
+ * @param emsg the error message. NULL if SUCCESS==GNUNET_OK
+ */
+void
+on_namestore_record_put_result(void *cls,
+ int32_t success,
+ const char *emsg)
+{
+ struct NamestoreBGTask *nbg = cls;
+
+ GNUNET_CONTAINER_heap_remove_node (nbg->node);
+ GNUNET_free (nbg);
+
+ if (GNUNET_NO == success)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_NS: records already in namestore\n");
+ return;
+ }
+ else if (GNUNET_YES == success)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_NS: records successfully put in namestore\n");
+ return;
+ }
+
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
+ "GNS_NS: Error putting records into namestore: %s\n", emsg);
+}
+
+static void
+handle_lookup_timeout(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ResolverHandle *rh = cls;
+
+ if (rh->timeout_cont)
+ rh->timeout_cont(rh->timeout_cont_cls, tc);
+}
+
+/**
+ * Processor for background lookups in the DHT
+ *
+ * @param cls closure (NULL)
+ * @param rd_count number of records found (not 0)
+ * @param rd record data
+ */
+static void
+background_lookup_result_processor(void *cls,
+ uint32_t rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd)
+{
+ //We could do sth verbose/more useful here but it doesn't make any difference
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_BG: background dht lookup finished. (%d results)\n",
+ rd_count);
+}
+
+/**
+ * Handle timeout for DHT requests
+ *
+ * @param cls the request handle as closure
+ * @param tc the task context
+ */
+static void
+dht_lookup_timeout(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ResolverHandle *rh = cls;
+ struct RecordLookupHandle *rlh = (struct RecordLookupHandle *)rh->proc_cls;
+ char new_name[MAX_DNS_NAME_LENGTH];
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: dht lookup for query %s (%llus)timed out.\n",
+ rh->id, rh->name, rh->timeout.rel_value);
+ /**
+ * Start resolution in bg
+ */
+ //strcpy(new_name, rh->name);
+ //memcpy(new_name+strlen(new_name), GNUNET_GNS_TLD, strlen(GNUNET_GNS_TLD));
+ GNUNET_snprintf(new_name, MAX_DNS_NAME_LENGTH, "%s.%s",
+ rh->name, GNUNET_GNS_TLD);
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Starting background lookup for %s type %d\n",
+ rh->id, new_name, rlh->record_type);
+
+ gns_resolver_lookup_record(rh->authority,
+ rh->private_local_zone,
+ rlh->record_type,
+ new_name,
+ NULL,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_NO,
+ &background_lookup_result_processor,
+ NULL);
+ rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+
+ GNUNET_DHT_get_stop (rh->get_handle);
+ rh->get_handle = NULL;
+ rh->proc(rh->proc_cls, rh, 0, NULL);
+}
+
+
+/**
+ * Function called when we get a result from the dht
+ * for our record query
+ *
+ * @param cls the request handle
+ * @param exp lifetime
+ * @param key the key the record was stored under
+ * @param get_path get path
+ * @param get_path_length get path length
+ * @param put_path put path
+ * @param put_path_length put path length
+ * @param type the block type
+ * @param size the size of the record
+ * @param data the record data
+ */
+static void
+process_record_result_dht(void* cls,
+ struct GNUNET_TIME_Absolute exp,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity *get_path,
+ unsigned int get_path_length,
+ const struct GNUNET_PeerIdentity *put_path,
+ unsigned int put_path_length,
+ enum GNUNET_BLOCK_Type type,
+ size_t size, const void *data)
+{
+ struct ResolverHandle *rh;
+ struct RecordLookupHandle *rlh;
+ struct GNSNameRecordBlock *nrb;
+ uint32_t num_records;
+ char* name = NULL;
+ char* rd_data = (char*)data;
+ int i;
+ int rd_size;
+
+ rh = (struct ResolverHandle *)cls;
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: got dht result (size=%d)\n", rh->id, size);
+
+ //FIXME maybe check expiration here, check block type
+
+
+ rlh = (struct RecordLookupHandle *) rh->proc_cls;
+ nrb = (struct GNSNameRecordBlock*)data;
+
+ /* stop lookup and timeout task */
+ GNUNET_DHT_get_stop (rh->get_handle);
+ rh->get_handle = NULL;
+
+ if (rh->dht_heap_node != NULL)
+ {
+ GNUNET_CONTAINER_heap_remove_node(rh->dht_heap_node);
+ rh->dht_heap_node = NULL;
+ }
+
+ if (rh->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(rh->timeout_task);
+ rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+
+ rh->get_handle = NULL;
+ name = (char*)&nrb[1];
+ num_records = ntohl(nrb->rd_count);
+ {
+ struct GNUNET_NAMESTORE_RecordData rd[num_records];
+ struct NamestoreBGTask *ns_heap_root;
+ struct NamestoreBGTask *namestore_bg_task;
+
+ rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
+ rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
+
+ if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
+ rd_data,
+ num_records,
+ rd))
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
+ "GNS_PHASE_REC-%llu: Error deserializing data!\n", rh->id);
+ return;
+ }
+
+ for (i=0; i<num_records; i++)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Got name: %s (wanted %s)\n",
+ rh->id, name, rh->name);
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Got type: %d (wanted %d)\n",
+ rh->id, rd[i].record_type, rlh->record_type);
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Got data length: %d\n",
+ rh->id, rd[i].data_size);
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Got flag %d\n",
+ rh->id, rd[i].flags);
+
+ if ((strcmp(name, rh->name) == 0) &&
+ (rd[i].record_type == rlh->record_type))
+ {
+ rh->answered++;
+ }
+
+ }
+
+ /**
+ * FIXME check pubkey against existing key in namestore?
+ * https://gnunet.org/bugs/view.php?id=2179
+ */
+ if (max_allowed_ns_tasks <=
+ GNUNET_CONTAINER_heap_get_size (ns_task_heap))
+ {
+ ns_heap_root = GNUNET_CONTAINER_heap_remove_root (ns_task_heap);
+ GNUNET_NAMESTORE_cancel (ns_heap_root->qe);
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Replacing oldest background ns task\n",
+ rh->id);
+ }
+
+ /* Save to namestore */
+ namestore_bg_task = GNUNET_malloc (sizeof (struct NamestoreBGTask));
+ namestore_bg_task->qe = GNUNET_NAMESTORE_record_put (namestore_handle,
+ &nrb->public_key,
+ name,
+ exp,
+ num_records,
+ rd,
+ &nrb->signature,
+ &on_namestore_record_put_result, //cont
+ namestore_bg_task);
+
+ namestore_bg_task->node = GNUNET_CONTAINER_heap_insert (ns_task_heap,
+ namestore_bg_task,
+ GNUNET_TIME_absolute_get().abs_value);
+
+
+ if (rh->answered)
+ rh->proc(rh->proc_cls, rh, num_records, rd);
+ else
+ rh->proc(rh->proc_cls, rh, 0, NULL);
+ }
+
+}
+
+
+/**
+ * Start DHT lookup for a (name -> query->record_type) record in
+ * rh->authority's zone
+ *
+ * @param rh the pending gns query context
+ */
+static void
+resolve_record_dht (struct ResolverHandle *rh)
+{
+ uint32_t xquery;
+ struct GNUNET_CRYPTO_ShortHashCode name_hash;
+ struct GNUNET_HashCode lookup_key;
+ struct GNUNET_HashCode name_hash_double;
+ struct GNUNET_HashCode zone_hash_double;
+ struct GNUNET_CRYPTO_HashAsciiEncoded lookup_key_string;
+ struct RecordLookupHandle *rlh = (struct RecordLookupHandle *)rh->proc_cls;
+ struct ResolverHandle *rh_heap_root;
+
+ GNUNET_CRYPTO_short_hash(rh->name, strlen(rh->name), &name_hash);
+ GNUNET_CRYPTO_short_hash_double(&name_hash, &name_hash_double);
+ GNUNET_CRYPTO_short_hash_double(&rh->authority, &zone_hash_double);
+ GNUNET_CRYPTO_hash_xor(&name_hash_double, &zone_hash_double, &lookup_key);
+ GNUNET_CRYPTO_hash_to_enc (&lookup_key, &lookup_key_string);
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: starting dht lookup for %s with key: %s\n",
+ rh->id, rh->name, (char*)&lookup_key_string);
+
+ //rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ rh->dht_heap_node = NULL;
+
+ if (rh->timeout.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+ {
+ /**
+ * Update timeout if necessary
+ */
+ if (rh->timeout_task == GNUNET_SCHEDULER_NO_TASK)
+ {
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Adjusting timeout\n", rh->id);
+ /*
+ * Set timeout for authority lookup phase to 1/2
+ */
+ rh->timeout_task = GNUNET_SCHEDULER_add_delayed(
+ GNUNET_TIME_relative_divide(rh->timeout, 2),
+ &handle_lookup_timeout,
+ rh);
+ }
+ //rh->timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
+ // &dht_lookup_timeout,
+ // rh);
+ rh->timeout_cont = &dht_lookup_timeout;
+ rh->timeout_cont_cls = rh;
+ }
+ else
+ {
+ if (max_allowed_background_queries <=
+ GNUNET_CONTAINER_heap_get_size (dht_lookup_heap))
+ {
+ rh_heap_root = GNUNET_CONTAINER_heap_remove_root (dht_lookup_heap);
+ GNUNET_DHT_get_stop(rh_heap_root->get_handle);
+ rh_heap_root->get_handle = NULL;
+ rh_heap_root->dht_heap_node = NULL;
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Replacing oldest background query for %s\n",
+ rh->id, rh_heap_root->name);
+ rh_heap_root->proc(rh_heap_root->proc_cls,
+ rh_heap_root,
+ 0,
+ NULL);
+ }
+ rh->dht_heap_node = GNUNET_CONTAINER_heap_insert (dht_lookup_heap,
+ rh,
+ GNUNET_TIME_absolute_get().abs_value);
+ }
+
+ xquery = htonl(rlh->record_type);
+
+ GNUNET_assert(rh->get_handle == NULL);
+ rh->get_handle = GNUNET_DHT_get_start(dht_handle,
+ GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
+ &lookup_key,
+ DHT_GNS_REPLICATION_LEVEL,
+ GNUNET_DHT_RO_NONE,
+ &xquery,
+ sizeof(xquery),
+ &process_record_result_dht,
+ rh);
+
+}
+
+
+/**
+ * Namestore calls this function if we have record for this name.
+ * (or with rd_count=0 to indicate no matches)
+ *
+ * @param cls the pending query
+ * @param key the key of the zone we did the lookup
+ * @param expiration expiration date of the namestore entry
+ * @param name the name for which we need an authority
+ * @param rd_count the number of records with 'name'
+ * @param rd the record data
+ * @param signature the signature of the authority for the record data
+ */
+static void
+process_record_result_ns (void* cls,
+ const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
+ struct GNUNET_TIME_Absolute expiration,
+ const char *name, unsigned int rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd,
+ const struct GNUNET_CRYPTO_RsaSignature *signature)
+{
+ struct ResolverHandle *rh;
+ struct RecordLookupHandle *rlh;
+ struct GNUNET_TIME_Relative remaining_time;
+ struct GNUNET_CRYPTO_ShortHashCode zone;
+ struct GNUNET_TIME_Absolute et;
+ unsigned int i;
+
+ rh = (struct ResolverHandle *) cls;
+ rlh = (struct RecordLookupHandle *)rh->proc_cls;
+
+ rh->namestore_task = NULL;
+ GNUNET_CRYPTO_short_hash(key,
+ sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+ &zone);
+ remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
+
+
+
+ rh->status = 0;
+
+ if (name != NULL)
+ {
+ rh->status |= RSL_RECORD_EXISTS;
+
+ if (remaining_time.rel_value == 0)
+ {
+ rh->status |= RSL_RECORD_EXPIRED;
+ }
+ }
+
+ if (rd_count == 0)
+ {
+ /**
+ * Lookup terminated and no results
+ */
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Namestore lookup for %s terminated without results\n",
+ rh->id, name);
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Record %s unknown in namestore\n",
+ rh->id, rh->name);
+ /**
+ * Our zone and no result? Cannot resolve TT
+ */
+ rh->proc(rh->proc_cls, rh, 0, NULL);
+ return;
+
+ }
+ else
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Processing additional result %s from namestore\n",
+ rh->id, name);
+ for (i=0; i<rd_count;i++)
+ {
+ if (rd[i].record_type != rlh->record_type)
+ continue;
+
+ if (ignore_pending_records &&
+ (rd[i].flags & GNUNET_NAMESTORE_RF_PENDING))
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Record %s is awaiting user confirmation. Skipping\n",
+ rh->id, name);
+ continue;
+ }
+
+ GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
+ et.abs_value = rd[i].expiration_time;
+ if ((GNUNET_TIME_absolute_get_remaining (et)).rel_value
+ == 0)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: This record is expired. Skipping\n",
+ rh->id);
+ continue;
+ }
+ rh->answered++;
+ }
+
+ /**
+ * no answers found
+ */
+ if (rh->answered == 0)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: No answers found. This is odd!\n", rh->id);
+ rh->proc(rh->proc_cls, rh, 0, NULL);
+ return;
+ }
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Found %d answer(s) to query in %d records!\n",
+ rh->id, rh->answered, rd_count);
+
+ rh->proc(rh->proc_cls, rh, rd_count, rd);
+ }
+}
+
+
+/**
+ * VPN redirect result callback
+ *
+ * @param cls the resolver handle
+ * @param af the requested address family
+ * @param address in_addr(6) respectively
+ */
+static void
+process_record_result_vpn (void* cls, int af, const void *address)
+{
+ struct ResolverHandle *rh = cls;
+ struct RecordLookupHandle *rlh;
+ struct GNUNET_NAMESTORE_RecordData rd;
+
+ rlh = (struct RecordLookupHandle *)rh->proc_cls;
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: Got answer from VPN to query!\n",
+ rh->id);
+ if (af == AF_INET)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Answer is IPv4!\n",
+ rh->id);
+ if (rlh->record_type != GNUNET_GNS_RECORD_A)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Requested record is not IPv4!\n",
+ rh->id);
+ rh->proc (rh->proc_cls, rh, 0, NULL);
+ return;
+ }
+ rd.record_type = GNUNET_GNS_RECORD_A;
+ rd.expiration_time = UINT64_MAX; /* FIXME: should probably pick something shorter... */
+ rd.data = address;
+ rd.data_size = sizeof (struct in_addr);
+ rd.flags = 0;
+ rh->proc (rh->proc_cls, rh, 1, &rd);
+ return;
+ }
+ else if (af == AF_INET6)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Answer is IPv6!\n",
+ rh->id);
+ if (rlh->record_type != GNUNET_GNS_RECORD_AAAA)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Requested record is not IPv6!\n",
+ rh->id);
+ rh->proc (rh->proc_cls, rh, 0, NULL);
+ return;
+ }
+ rd.record_type = GNUNET_GNS_RECORD_AAAA;
+ rd.expiration_time = UINT64_MAX; /* FIXME: should probably pick something shorter... */
+ rd.data = address;
+ rd.data_size = sizeof (struct in6_addr);
+ rd.flags = 0;
+ rh->proc (rh->proc_cls, rh, 1, &rd);
+ return;
+ }
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Got garbage from VPN!\n",
+ rh->id);
+ rh->proc (rh->proc_cls, rh, 0, NULL);
+}
+
+
+
+
+/**
+ * Process VPN lookup result for record
+ *
+ * @param cls the record lookup handle
+ * @param rh resolver handle
+ * @param rd_count number of results (1)
+ * @param rd record data containing the result
+ */
+static void
+handle_record_vpn (void* cls, struct ResolverHandle *rh,
+ unsigned int rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd)
+{
+ struct RecordLookupHandle* rlh = (struct RecordLookupHandle*) cls;