X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fgns%2Fgnunet-service-gns_resolver.c;h=c881b6b83cbee2ba9c930d41e65913d305b7d3b1;hb=f416ed5cff91ff66dadfb4797562ec5c5a0adc21;hp=c36fa8d17a6616b31313f9d9bfaa59453a3ad35c;hpb=37284be800eef44babef8fc3cc0461c0d3cbea67;p=oweals%2Fgnunet.git diff --git a/src/gns/gnunet-service-gns_resolver.c b/src/gns/gnunet-service-gns_resolver.c index c36fa8d17..c881b6b83 100644 --- a/src/gns/gnunet-service-gns_resolver.c +++ b/src/gns/gnunet-service-gns_resolver.c @@ -20,49 +20,31 @@ /** * @file gns/gnunet-service-gns_resolver.c - * @brief GNUnet GNS resolver logic + * @brief GNU Name System resolver logic * @author Martin Schanzenbach * @author Christian Grothoff * * TODO: - * - GNS: handle CNAME records (idea: manipulate rh->name) - * - GNS: handle VPN records (easy) * - GNS: handle special SRV names --- no delegation, direct lookup; - * can likely be done in 'resolver_lookup_get_next_label'. - * - recursive DNS resolution - * - shortening triggers - * - revocation checks (make optional: privacy!) - * - * Issues: - * - We currently go to the DHT simply if we find no local reply; this - * is incorrect; the correct rules for going to DHT are: - * - * 1. The entry in the DHT is RSL_RECORD_EXPIRED OR - * 2. No entry in the NS existed AND - * 3. The zone queried is not the local resolver's zone AND - * 4. The name that was looked up is '+' - * because if it was any other canonical name we either already queried - * the DHT for the authority in the authority lookup phase (and thus - * would already have an entry in the NS for the record) - * 5. We are not in cache only mode - * - * - We currently never look "into" the records; for example, - * MX, SRV and SOA records may include ".+" that need to be - * handled - * + * can likely be done in 'resolver_lookup_get_next_label'. (#3003) + * - revocation checks (use REVOCATION service!), (#3004) + * - DNAME support (#3005) */ #include "platform.h" #include "gnunet_util_lib.h" -#include "gnunet_transport_service.h" #include "gnunet_dnsstub_lib.h" #include "gnunet_dht_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_namecache_service.h" #include "gnunet_namestore_service.h" #include "gnunet_dns_service.h" #include "gnunet_resolver_service.h" +#include "gnunet_revocation_service.h" #include "gnunet_dnsparser_lib.h" #include "gnunet_gns_service.h" #include "gns.h" #include "gnunet-service-gns_resolver.h" +#include "gnunet-service-gns_shorten.h" #include "gnunet_vpn_service.h" @@ -76,6 +58,11 @@ */ #define DNS_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) +/** + * Default timeout for VPN redirections. + */ +#define VPN_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) + /** * DHT replication level */ @@ -109,10 +96,10 @@ struct AuthorityChain struct GNS_ResolverHandle *rh; /** - * label/name corresponding to the authority + * label/name corresponding to the authority */ char *label; - + /** * #GNUNET_YES if the authority was a GNS authority, * #GNUNET_NO if the authority was a DNS authority. @@ -126,9 +113,9 @@ struct AuthorityChain { /** - * The zone of the GNS authority + * The zone of the GNS authority */ - struct GNUNET_CRYPTO_EccPublicKey gns_authority; + struct GNUNET_CRYPTO_EcdsaPublicKey gns_authority; struct { @@ -149,54 +136,7 @@ struct AuthorityChain } dns_authority; } authority_info; - -}; - - -/** - * Resolution status indicator - */ -enum ResolutionStatus -{ - /** - * the name to lookup exists - */ - RSL_RECORD_EXISTS = 1, - /** - * the name in the record expired - */ - RSL_RECORD_EXPIRED = 2, - - /** - * resolution timed out - */ - RSL_TIMED_OUT = 4, - - /** - * Found VPN delegation - */ - RSL_DELEGATE_VPN = 8, - - /** - * Found NS delegation - */ - RSL_DELEGATE_NS = 16, - - /** - * Found PKEY delegation - */ - RSL_DELEGATE_PKEY = 32, - - /** - * Found CNAME record - */ - RSL_CNAME_FOUND = 64, - - /** - * Found PKEY has been revoked - */ - RSL_PKEY_REVOKED = 128 }; @@ -228,7 +168,7 @@ struct DnsResult uint64_t expiration_time; /** - * Number of bytes in 'data'. + * Number of bytes in @e data. */ size_t data_size; @@ -240,47 +180,80 @@ struct DnsResult }; +/** + * Closure for #vpn_allocation_cb. + */ +struct VpnContext +{ + + /** + * Which resolution process are we processing. + */ + struct GNS_ResolverHandle *rh; + + /** + * Handle to the VPN request that we were performing. + */ + struct GNUNET_VPN_RedirectionRequest *vpn_request; + + /** + * Number of records serialized in @e rd_data. + */ + unsigned int rd_count; + + /** + * Serialized records. + */ + char *rd_data; + + /** + * Number of bytes in @e rd_data. + */ + size_t rd_data_size; +}; + + /** * Handle to a currenty pending resolution. On result (positive or - * negative) the #GNS_ResultProcessor is called. + * negative) the #GNS_ResultProcessor is called. */ struct GNS_ResolverHandle { /** - * DLL + * DLL */ struct GNS_ResolverHandle *next; /** - * DLL + * DLL */ struct GNS_ResolverHandle *prev; /** - * The top-level GNS authoritative zone to query + * The top-level GNS authoritative zone to query */ - struct GNUNET_CRYPTO_EccPublicKey authority_zone; + struct GNUNET_CRYPTO_EcdsaPublicKey authority_zone; /** - * called when resolution phase finishes + * called when resolution phase finishes */ GNS_ResultProcessor proc; - + /** - * closure passed to proc + * closure passed to proc */ void* proc_cls; /** - * Handle for DHT lookups. should be NULL if no lookups are in progress + * Handle for DHT lookups. should be NULL if no lookups are in progress */ struct GNUNET_DHT_GetHandle *get_handle; /** * Handle to a VPN request, NULL if none is active. */ - struct GNUNET_VPN_RedirectionRequest *vpn_handle; + struct VpnContext *vpn_ctx; /** * Socket for a DNS request, NULL if none is active. @@ -293,9 +266,14 @@ struct GNS_ResolverHandle struct GNUNET_RESOLVER_RequestHandle *std_resolve; /** - * Pending Namestore task + * Pending Namecache lookup task + */ + struct GNUNET_NAMECACHE_QueueEntry *namecache_qe; + + /** + * Pending revocation check. */ - struct GNUNET_NAMESTORE_QueueEntry *namestore_qe; + struct GNUNET_REVOCATION_Query *rev_check; /** * Heap node associated with this lookup. Used to limit number of @@ -304,19 +282,19 @@ struct GNS_ResolverHandle struct GNUNET_CONTAINER_HeapNode *dht_heap_node; /** - * DLL to store the authority chain + * DLL to store the authority chain */ struct AuthorityChain *ac_head; /** - * DLL to store the authority chain + * DLL to store the authority chain */ struct AuthorityChain *ac_tail; /** * Private key of the shorten zone, NULL to not shorten. */ - struct GNUNET_CRYPTO_EccPrivateKey *shorten_key; + struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key; /** * ID of a task associated with the resolution process. @@ -324,7 +302,7 @@ struct GNS_ResolverHandle GNUNET_SCHEDULER_TaskIdentifier task_id; /** - * The name to resolve + * The name to resolve */ char *name; @@ -344,7 +322,7 @@ struct GNS_ResolverHandle size_t name_resolution_pos; /** - * Use only cache + * Use only cache */ int only_cached; @@ -355,7 +333,7 @@ struct GNS_ResolverHandle /** * We increment the loop limiter for each step in a recursive - * resolution. If it passes our threshold (i.e. due to + * resolution. If it passes our threshold (i.e. due to * self-recursion in the resolution, i.e CNAME fun), we stop. */ unsigned int loop_limiter; @@ -364,63 +342,33 @@ struct GNS_ResolverHandle /** - * Handle for a PSEU lookup used to shorten names. + * Active namestore caching operations. */ -struct GetPseuAuthorityHandle +struct CacheOps { - /** - * DLL - */ - struct GetPseuAuthorityHandle *next; - - /** - * DLL - */ - struct GetPseuAuthorityHandle *prev; - - /** - * Private key of the (shorten) zone to store the resulting - * pseudonym in. - */ - struct GNUNET_CRYPTO_EccPrivateKey shorten_zone_key; - - /** - * Original label (used if no PSEU record is found). - */ - char label[GNUNET_DNSPARSER_MAX_LABEL_LENGTH + 1]; - - /** - * Label we are currently trying out (during #perform_pseu_lookup). - */ - char *current_label; - - /** - * The zone for which we are trying to find the PSEU record. - */ - struct GNUNET_CRYPTO_EccPublicKey target_zone; /** - * Handle for DHT lookups. Should be NULL if no lookups are in progress + * Organized in a DLL. */ - struct GNUNET_DHT_GetHandle *get_handle; + struct CacheOps *next; /** - * Handle to namestore request + * Organized in a DLL. */ - struct GNUNET_NAMESTORE_QueueEntry *namestore_task; + struct CacheOps *prev; /** - * Task to abort DHT lookup operation. + * Pending Namestore caching task. */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; + struct GNUNET_NAMECACHE_QueueEntry *namecache_qe_cache; }; /** - * Our handle to the namestore service + * Our handle to the namecache service */ -static struct GNUNET_NAMESTORE_Handle *namestore_handle; +static struct GNUNET_NAMECACHE_Handle *namecache_handle; /** * Our handle to the vpn service @@ -448,36 +396,37 @@ static struct GNUNET_CONTAINER_Heap *dht_lookup_heap; static unsigned long long max_allowed_background_queries; /** - * Head of PSEU/shorten operations list. + * Head of resolver lookup list */ -struct GetPseuAuthorityHandle *gph_head; +static struct GNS_ResolverHandle *rlh_head; /** - * Tail of PSEU/shorten operations list. + * Tail of resolver lookup list */ -struct GetPseuAuthorityHandle *gph_tail; +static struct GNS_ResolverHandle *rlh_tail; /** - * Head of resolver lookup list + * Organized in a DLL. */ -static struct GNS_ResolverHandle *rlh_head; +static struct CacheOps *co_head; /** - * Tail of resolver lookup list + * Organized in a DLL. */ -static struct GNS_ResolverHandle *rlh_tail; +static struct CacheOps *co_tail; + /** * Global configuration. */ static const struct GNUNET_CONFIGURATION_Handle *cfg; - +#if 0 /** * Check if name is in srv format (_x._y.xxx) * * @param name - * @return GNUNET_YES if true + * @return #GNUNET_YES if true */ static int is_srv (const char *name) @@ -501,6 +450,7 @@ is_srv (const char *name) GNUNET_free (ndup); return ret; } +#endif /** @@ -516,7 +466,7 @@ is_srv (const char *name) * _f.bar = not canonical * * @param name the name to test - * @return GNUNET_YES if canonical + * @return #GNUNET_YES if canonical */ static int is_canonical (const char *name) @@ -529,7 +479,7 @@ is_canonical (const char *name) if ('_' != name[0]) return GNUNET_NO; pos = &name[1]; - while (NULL != (dot = strchr (pos, '.'))) + while (NULL != (dot = strchr (pos, '.'))) if ('_' != dot[1]) return GNUNET_NO; else @@ -537,776 +487,36 @@ is_canonical (const char *name) return GNUNET_YES; } - -/* ******************** Shortening logic ************************ */ - - -/** - * Cleanup a 'struct GetPseuAuthorityHandle', terminating all - * pending activities. - * - * @param gph handle to terminate - */ -static void -free_get_pseu_authority_handle (struct GetPseuAuthorityHandle *gph) -{ - if (NULL != gph->get_handle) - { - GNUNET_DHT_get_stop (gph->get_handle); - gph->get_handle = NULL; - } - if (NULL != gph->namestore_task) - { - GNUNET_NAMESTORE_cancel (gph->namestore_task); - gph->namestore_task = NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != gph->timeout_task) - { - GNUNET_SCHEDULER_cancel (gph->timeout_task); - gph->timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, gph); - GNUNET_free_non_null (gph->current_label); - GNUNET_free (gph); -} - - -/** - * Continuation for pkey record creation (shorten) - * - * @param cls a GetPseuAuthorityHandle - * @param success unused - * @param emsg unused - */ -static void -create_pkey_cont (void* cls, - int32_t success, - const char *emsg) -{ - struct GetPseuAuthorityHandle* gph = cls; - - gph->namestore_task = NULL; - free_get_pseu_authority_handle (gph); -} - - -/** - * 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 rd_count the number of records with 'name' - * @param rd the record data - */ -static void -process_pseu_lookup_ns (void *cls, - unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd); - - -/** - * We obtained a result for our query to the shorten zone from - * the namestore. Try to decrypt. - * - * @param cls the handle to our shorten operation - * @param block resulting encrypted block - */ -static void -process_pseu_block_ns (void *cls, - const struct GNUNET_NAMESTORE_Block *block) -{ - struct GetPseuAuthorityHandle *gph = cls; - struct GNUNET_CRYPTO_EccPublicKey pub; - - gph->namestore_task = NULL; - if (NULL == block) - { - process_pseu_lookup_ns (gph, 0, NULL); - return; - } - GNUNET_CRYPTO_ecc_key_get_public (&gph->shorten_zone_key, - &pub); - if (GNUNET_OK != - GNUNET_NAMESTORE_block_decrypt (block, - &pub, - gph->current_label, - &process_pseu_lookup_ns, - gph)) - { - GNUNET_break (0); - free_get_pseu_authority_handle (gph); - return; - } -} - - -/** - * Lookup in the namestore for the shorten zone the given label. - * - * @param gph the handle to our shorten operation - * @param label the label to lookup - */ -static void -perform_pseu_lookup (struct GetPseuAuthorityHandle *gph, - const char *label) -{ - struct GNUNET_CRYPTO_EccPublicKey pub; - struct GNUNET_HashCode query; - - GNUNET_CRYPTO_ecc_key_get_public (&gph->shorten_zone_key, - &pub); - GNUNET_free_non_null (gph->current_label); - gph->current_label = GNUNET_strdup (label); - GNUNET_NAMESTORE_query_from_public_key (&pub, - label, - &query); - gph->namestore_task = GNUNET_NAMESTORE_lookup_block (namestore_handle, - &query, - &process_pseu_block_ns, - gph); -} - - -/** - * 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 rd_count the number of records with 'name' - * @param rd the record data - */ -static void -process_pseu_lookup_ns (void *cls, - unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd) -{ - struct GetPseuAuthorityHandle *gph = cls; - struct GNUNET_NAMESTORE_RecordData new_pkey; - - gph->namestore_task = NULL; - if (rd_count > 0) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Name `%s' already taken, cannot shorten.\n", - gph->current_label); - /* if this was not yet the original label, try one more - time, this time not using PSEU but the original label */ - if (0 == strcmp (gph->current_label, - gph->label)) - { - free_get_pseu_authority_handle (gph); - } - else - { - perform_pseu_lookup (gph, gph->label); - } - return; - } - /* name is available */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shortening `%s' to `%s'\n", - GNUNET_NAMESTORE_z2s (&gph->target_zone), - gph->current_label); - new_pkey.expiration_time = UINT64_MAX; - new_pkey.data_size = sizeof (struct GNUNET_CRYPTO_EccPublicKey); - new_pkey.data = &gph->target_zone; - new_pkey.record_type = GNUNET_NAMESTORE_TYPE_PKEY; - new_pkey.flags = GNUNET_NAMESTORE_RF_AUTHORITY - | GNUNET_NAMESTORE_RF_PRIVATE - | GNUNET_NAMESTORE_RF_PENDING; - gph->namestore_task - = GNUNET_NAMESTORE_records_store (namestore_handle, - &gph->shorten_zone_key, - gph->current_label, - 1, &new_pkey, - &create_pkey_cont, gph); -} - - -/** - * Process result of a DHT lookup for a PSEU record. - * - * @param gph the handle to our shorten operation - * @param pseu the pseu result or NULL - */ -static void -process_pseu_result (struct GetPseuAuthorityHandle* gph, - const char *pseu) -{ - if (NULL == pseu) - { - /* no PSEU found, try original label */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No PSEU found, trying original label `%s' instead.\n", - gph->label); - perform_pseu_lookup (gph, gph->label); - return; - } - /* check if 'pseu' is taken */ - perform_pseu_lookup (gph, pseu); -} - - -/** - * Handle timeout for DHT request during shortening. - * - * @param cls the request handle as closure - * @param tc the task context - */ -static void -handle_auth_discovery_timeout (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GetPseuAuthorityHandle *gph = cls; - - gph->timeout_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DHT lookup for PSEU query timed out.\n"); - GNUNET_DHT_get_stop (gph->get_handle); - gph->get_handle = NULL; - process_pseu_result (gph, NULL); -} - - -/** - * Handle decrypted records from DHT result. - * - * @param cls closure with our 'struct GetPseuAuthorityHandle' - * @param rd_count number of entries in 'rd' array - * @param rd array of records with data to store - */ -static void -process_auth_records (void *cls, - unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd) -{ - struct GetPseuAuthorityHandle *gph = cls; - unsigned int i; - - for (i=0; i < rd_count; i++) - { - if (GNUNET_NAMESTORE_TYPE_PSEU == rd[i].record_type) - { - /* found pseu */ - process_pseu_result (gph, - (const char *) rd[i].data); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No PSEU record found in DHT reply.\n"); - process_pseu_result (gph, NULL); -} - - -/** - * Function called when we find a PSEU entry in the DHT - * - * @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_auth_discovery_dht_result (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 GetPseuAuthorityHandle *gph = cls; - const struct GNUNET_NAMESTORE_Block *block; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got DHT result for PSEU request\n"); - GNUNET_DHT_get_stop (gph->get_handle); - gph->get_handle = NULL; - GNUNET_SCHEDULER_cancel (gph->timeout_task); - gph->timeout_task = GNUNET_SCHEDULER_NO_TASK; - - if (NULL == data) - { - /* is this allowed!? */ - GNUNET_break (0); - process_pseu_result (gph, NULL); - return; - } - if (size < sizeof (struct GNUNET_NAMESTORE_Block)) - { - /* how did this pass DHT block validation!? */ - GNUNET_break (0); - process_pseu_result (gph, NULL); - return; - } - block = data; - if (size != - ntohs (block->purpose.size) + - sizeof (struct GNUNET_CRYPTO_EccPublicKey) + - sizeof (struct GNUNET_CRYPTO_EccSignature)) - { - /* how did this pass DHT block validation!? */ - GNUNET_break (0); - process_pseu_result (gph, NULL); - return; - } - if (GNUNET_OK != - GNUNET_NAMESTORE_block_decrypt (block, - &gph->target_zone, - GNUNET_GNS_TLD_PLUS, - &process_auth_records, - gph)) - { - /* other peer encrypted invalid block, complain */ - GNUNET_break_op (0); - process_pseu_result (gph, NULL); - return; - } -} - - -/** - * Callback called by namestore for a zone to name result. We're - * trying to see if a short name for a given zone already exists. - * - * @param cls the closure - * @param zone_key the zone we queried - * @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 - */ -static void -process_zone_to_name_discover (void *cls, - const struct GNUNET_CRYPTO_EccPrivateKey *zone_key, - const char *name, - unsigned int rd_len, - const struct GNUNET_NAMESTORE_RecordData *rd) -{ - struct GetPseuAuthorityHandle* gph = cls; - struct GNUNET_HashCode lookup_key; - - gph->namestore_task = NULL; - if (0 != rd_len) - { - /* we found a match in our own zone */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shortening aborted, name `%s' already reserved for the zone\n", - name); - free_get_pseu_authority_handle (gph); - return; - } - /* record does not yet exist, go into DHT to find PSEU record */ - GNUNET_NAMESTORE_query_from_public_key (&gph->target_zone, - GNUNET_GNS_TLD_PLUS, - &lookup_key); - gph->timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT, - &handle_auth_discovery_timeout, - gph); - gph->get_handle = GNUNET_DHT_get_start (dht_handle, - GNUNET_BLOCK_TYPE_GNS_NAMERECORD, - &lookup_key, - DHT_GNS_REPLICATION_LEVEL, - GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, - NULL, 0, - &process_auth_discovery_dht_result, - gph); -} - - -/** - * Start shortening algorithm, try to allocate a nice short - * canonical name for @a pub in @a shorten_zone, using - * @a original_label as one possible suggestion. - * - * @param original_label original label for the zone - * @param pub public key of the zone to shorten - * @param shorten_zone private key of the target zone for the new record - */ -static void -start_shorten (const char *original_label, - const struct GNUNET_CRYPTO_EccPublicKey *pub, - const struct GNUNET_CRYPTO_EccPrivateKey *shorten_zone) -{ - struct GetPseuAuthorityHandle *gph; - - if (strlen (original_label) > GNUNET_DNSPARSER_MAX_LABEL_LENGTH) - { - GNUNET_break (0); - return; - } - gph = GNUNET_new (struct GetPseuAuthorityHandle); - gph->shorten_zone_key = *shorten_zone; - gph->target_zone = *pub; - strcpy (gph->label, original_label); - GNUNET_CONTAINER_DLL_insert (gph_head, gph_tail, gph); - /* first, check if we *already* have a record for this zone */ - gph->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle, - shorten_zone, - pub, - &process_zone_to_name_discover, - gph); -} - - /* ************************** Resolution **************************** */ -#if 0 - - -/** - * 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 = rh->proc_cls; - struct GNUNET_NAMESTORE_RecordData rd; - - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC_VPN-%llu: Got answer from VPN to query!\n", - rh->id); - if (AF_INET == af) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC-%llu: Answer is IPv4!\n", - rh->id); - if (GNUNET_DNSPARSER_TYPE_A != rlh->record_type) - { - 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_DNSPARSER_TYPE_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_INET6 == af) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC-%llu: Answer is IPv6!\n", - rh->id); - if (GNUNET_DNSPARSER_TYPE_AAAA != rlh->record_type) - { - 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_DNSPARSER_TYPE_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); -} - - - - - -/** - * The final phase of resoution. - * We found a VPN RR and want to request an IPv4/6 address - * - * @param rh the pending lookup handle - * @param rd_count length of record data - * @param rd record data containing VPN RR - */ -static void -resolve_record_vpn (struct ResolverHandle *rh, - unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd) -{ - struct RecordLookupHandle *rlh = rh->proc_cls; - struct GNUNET_HashCode serv_desc; - struct GNUNET_TUN_GnsVpnRecord* vpn; - int af; - - /* We cancel here as to not include the ns lookup in the timeout */ - if (GNUNET_SCHEDULER_NO_TASK != rh->timeout_task) - { - GNUNET_SCHEDULER_cancel(rh->timeout_task); - rh->timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - /* Start shortening */ - if ((NULL != rh->priv_key) && - (GNUNET_YES == is_canonical (rh->name))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC_VPN-%llu: Trying to shorten authority chain\n", - rh->id); - start_shorten (rh->authority_chain_head, - rh->priv_key); - } - - vpn = (struct GNUNET_TUN_GnsVpnRecord*)rd->data; - GNUNET_CRYPTO_hash ((char*)&vpn[1], - strlen ((char*)&vpn[1]) + 1, - &serv_desc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC_VPN-%llu: proto %hu peer %s!\n", - rh->id, - ntohs (vpn->proto), - GNUNET_h2s (&vpn->peer)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_REC_VPN-%llu: service %s -> %s!\n", - rh->id, - (char*)&vpn[1], - GNUNET_h2s (&serv_desc)); - rh->proc = &handle_record_vpn; - if (GNUNET_DNSPARSER_TYPE_A == rlh->record_type) - af = AF_INET; - else - af = AF_INET6; -#ifndef WINDOWS - if (NULL == vpn_handle) - { - vpn_handle = GNUNET_VPN_connect (cfg); - if (NULL == vpn_handle) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_PHASE_INIT: Error connecting to VPN!\n"); - finish_lookup (rh, rh->proc_cls, 0, NULL); - return; - } - } - - rh->vpn_handle = GNUNET_VPN_redirect_to_peer (vpn_handle, - af, ntohs (vpn->proto), - (struct GNUNET_PeerIdentity *)&vpn->peer, - &serv_desc, - GNUNET_NO, //nac - GNUNET_TIME_UNIT_FOREVER_ABS, //FIXME - &process_record_result_vpn, - rh); -#else - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error connecting to VPN (not available on W32 yet)\n"); - finish_lookup (rh, rh->proc_cls, 0, NULL); -#endif -} - - -//FIXME maybe define somewhere else? -#define MAX_SOA_LENGTH sizeof(uint32_t)+sizeof(uint32_t)+sizeof(uint32_t)+sizeof(uint32_t)\ - +(GNUNET_DNSPARSER_MAX_NAME_LENGTH*2) -#define MAX_MX_LENGTH sizeof(uint16_t)+GNUNET_DNSPARSER_MAX_NAME_LENGTH -#define MAX_SRV_LENGTH (sizeof(uint16_t)*3)+GNUNET_DNSPARSER_MAX_NAME_LENGTH - - /** - * Exands a name ending in .+ with the zone of origin. - * FIXME: funky api: 'dest' must be large enough to hold - * the result; this is a bit yucky... + * Expands a name ending in .+ with the zone of origin. * - * @param dest destination buffer - * @param src the .+ name - * @param repl the string to replace the + with - */ -static void -expand_plus (char* dest, - const char* src, - const char* repl) -{ - char* pos; - size_t s_len = strlen (src) + 1; - - //Eh? I guess this is at least strlen ('x.+') == 3 FIXME - if (3 > s_len) - { - /* no postprocessing */ - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_POSTPROCESS: %s too short\n", src); - memcpy (dest, src, s_len); - return; - } - if (0 == strcmp (src + s_len - 3, ".+")) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_POSTPROCESS: Expanding .+ in %s\n", - src); - memset (dest, 0, s_len + strlen (repl) + strlen(GNUNET_GNS_TLD)); - strcpy (dest, src); - pos = dest + s_len - 2; - strcpy (pos, repl); - pos += strlen (repl); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GNS_POSTPROCESS: Expanded to %s\n", - dest); - } - else - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_POSTPROCESS: No postprocessing for %s\n", src); - memcpy (dest, src, s_len); - } -} - - -/** - * finish lookup + * @param rh resolution context + * @param name name to modify (to be free'd or returned) + * @return updated name */ -static void -finish_lookup (struct ResolverHandle *rh, - struct RecordLookupHandle* rlh, - unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd) +static char * +translate_dot_plus (struct GNS_ResolverHandle *rh, + char *name) { - unsigned int i; - char new_rr_data[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; - char new_mx_data[MAX_MX_LENGTH]; - char new_soa_data[MAX_SOA_LENGTH]; - char new_srv_data[MAX_SRV_LENGTH]; - struct GNUNET_TUN_DnsSrvRecord *old_srv; - struct GNUNET_TUN_DnsSrvRecord *new_srv; - struct GNUNET_TUN_DnsSoaRecord *old_soa; - struct GNUNET_TUN_DnsSoaRecord *new_soa; - struct GNUNET_NAMESTORE_RecordData p_rd[rd_count]; - char* repl_string; - char* pos; - unsigned int offset; - - if (GNUNET_SCHEDULER_NO_TASK != rh->timeout_task) - { - GNUNET_SCHEDULER_cancel(rh->timeout_task); - rh->timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - - GNUNET_CONTAINER_DLL_remove (rlh_head, rlh_tail, rh); - - if (0 < rd_count) - memcpy(p_rd, rd, rd_count*sizeof(struct GNUNET_NAMESTORE_RecordData)); - - for (i = 0; i < rd_count; i++) - { - - if ((GNUNET_DNSPARSER_TYPE_NS != rd[i].record_type) && - (GNUNET_DNSPARSER_TYPE_PTR != rd[i].record_type) && - (GNUNET_DNSPARSER_TYPE_CNAME != rd[i].record_type) && - (GNUNET_DNSPARSER_TYPE_MX != rd[i].record_type) && - (GNUNET_DNSPARSER_TYPE_SOA != rd[i].record_type) && - (GNUNET_DNSPARSER_TYPE_SRV != rd[i].record_type)) - { - p_rd[i].data = rd[i].data; - continue; - } - - /** - * for all those records we 'should' - * also try to resolve the A/AAAA records (RFC1035) - * This is a feature and not important - */ - - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "GNS_POSTPROCESS: Postprocessing\n"); - if (0 == strcmp(rh->name, GNUNET_GNS_MASTERZONE_STR)) - repl_string = rlh->name; - else - repl_string = rlh->name+strlen(rh->name)+1; - - offset = 0; - if (GNUNET_DNSPARSER_TYPE_MX == rd[i].record_type) - { - memcpy (new_mx_data, (char*)rd[i].data, sizeof(uint16_t)); - offset = sizeof (uint16_t); - pos = new_mx_data + offset; - // FIXME: how do we know that 'pos' has enough space for the new name? - expand_plus (pos, (char*)rd[i].data+sizeof(uint16_t), - repl_string); - offset += strlen(new_mx_data+sizeof(uint16_t)) + 1; - p_rd[i].data = new_mx_data; - p_rd[i].data_size = offset; - } - else if (GNUNET_DNSPARSER_TYPE_SRV == rd[i].record_type) - { - /* - * Prio, weight and port - */ - new_srv = (struct GNUNET_TUN_DnsSrvRecord*)new_srv_data; - old_srv = (struct GNUNET_TUN_DnsSrvRecord*)rd[i].data; - new_srv->prio = old_srv->prio; - new_srv->weight = old_srv->weight; - new_srv->port = old_srv->port; - // FIXME: how do we know that '&new_srv[1]' has enough space for the new name? - expand_plus((char*)&new_srv[1], (char*)&old_srv[1], - repl_string); - p_rd[i].data = new_srv_data; - p_rd[i].data_size = sizeof (struct GNUNET_TUN_DnsSrvRecord) + strlen ((char*)&new_srv[1]) + 1; - } - else if (GNUNET_DNSPARSER_TYPE_SOA == rd[i].record_type) - { - /* expand mname and rname */ - old_soa = (struct GNUNET_TUN_DnsSoaRecord*)rd[i].data; - new_soa = (struct GNUNET_TUN_DnsSoaRecord*)new_soa_data; - memcpy (new_soa, old_soa, sizeof (struct GNUNET_TUN_DnsSoaRecord)); - // FIXME: how do we know that 'new_soa[1]' has enough space for the new name? - expand_plus((char*)&new_soa[1], (char*)&old_soa[1], repl_string); - offset = strlen ((char*)&new_soa[1]) + 1; - // FIXME: how do we know that 'new_soa[1]' has enough space for the new name? - expand_plus((char*)&new_soa[1] + offset, - (char*)&old_soa[1] + strlen ((char*)&old_soa[1]) + 1, - repl_string); - p_rd[i].data_size = sizeof (struct GNUNET_TUN_DnsSoaRecord) - + offset - + strlen ((char*)&new_soa[1] + offset); - p_rd[i].data = new_soa_data; - } - else - { - pos = new_rr_data; - // FIXME: how do we know that 'rd[i].data' has enough space for the new name? - expand_plus(pos, (char*)rd[i].data, repl_string); - p_rd[i].data_size = strlen(new_rr_data)+1; - p_rd[i].data = new_rr_data; - } - - } - - rlh->proc(rlh->proc_cls, rd_count, p_rd); - GNUNET_free(rlh); - free_resolver_handle (rh); + char *ret; + size_t s_len = strlen (name); + + if (0 != strcmp (&name[s_len - 2], + ".+")) + return name; /* did not end in ".+" */ + GNUNET_assert (GNUNET_YES == rh->ac_tail->gns_authority); + GNUNET_asprintf (&ret, + "%.*s.%s", + (int) (s_len - 2), + name, + GNUNET_GNSRECORD_pkey_to_zkey (&rh->ac_tail->authority_info.gns_authority)); + GNUNET_free (name); + return ret; } -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - - /** * Task scheduled to asynchronously fail a resolution. * @@ -1324,19 +534,26 @@ fail_resolution (void *cls, GNS_resolver_lookup_cancel (rh); } -#ifdef WINDOWS -/* Don't have this on W32, here's a naive implementation */ -void *memrchr (const void *s, int c, size_t n) + +#if (defined WINDOWS) || (defined DARWIN) +/* Don't have this on W32, here's a naive implementation + * Was somehow removed on OS X ... */ +void * +memrchr (const void *s, + int c, + size_t n) { - size_t i; - unsigned char *ucs = (unsigned char *) s; + const unsigned char *ucs = s; + ssize_t i; + for (i = n - 1; i >= 0; i--) - if (ucs[i] == c) + if (c == (int) ucs[i]) return (void *) &ucs[i]; return NULL; } #endif + /** * Get the next, rightmost label from the name that we are trying to resolve, * and update the resolution position accordingly. @@ -1359,7 +576,7 @@ resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh) /* done, this was the last one */ len = rh->name_resolution_pos; rp = rh->name; - rh->name_resolution_pos = 0; + rh->name_resolution_pos = 0; } else { @@ -1367,8 +584,8 @@ resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh) len = rh->name_resolution_pos - (dot - rh->name) - 1; rp = dot + 1; rh->name_resolution_pos = dot - rh->name; - } - return GNUNET_strndup (rp, len); + } + return GNUNET_strndup (rp, len); } @@ -1388,7 +605,7 @@ transmit_lookup_dns_result (struct GNS_ResolverHandle *rh) for (pos = rh->dns_result_head; NULL != pos; pos = pos->next) n++; { - struct GNUNET_NAMESTORE_RecordData rd[n]; + struct GNUNET_GNSRECORD_Data rd[n]; i = 0; for (pos = rh->dns_result_head; NULL != pos; pos = pos->next) @@ -1398,15 +615,20 @@ transmit_lookup_dns_result (struct GNS_ResolverHandle *rh) rd[i].record_type = pos->record_type; if (0 == pos->expiration_time) { - rd[i].flags = GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION; + rd[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; rd[i].expiration_time = 0; } else { - rd[i].flags = GNUNET_NAMESTORE_RF_NONE; + rd[i].flags = GNUNET_GNSRECORD_RF_NONE; rd[i].expiration_time = pos->expiration_time; } - } + i++; + } + GNUNET_assert (i == n); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting standard DNS result with %u records\n", + n); rh->proc (rh->proc_cls, n, rd); @@ -1449,7 +671,7 @@ add_dns_result (struct GNS_ResolverHandle *rh, * We had to do a DNS lookup. Convert the result (if any) and return * it. * - * @param cls closure with the 'struct GNS_ResolverHandle' + * @param cls closure with the `struct GNS_ResolverHandle` * @param addr one of the addresses of the host, NULL for the last address * @param addrlen length of the address */ @@ -1462,9 +684,12 @@ handle_dns_result (void *cls, const struct sockaddr_in *sa4; const struct sockaddr_in6 *sa6; - rh->std_resolve = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u bytes of DNS IP data\n", + addrlen); if (NULL == addr) { + rh->std_resolve = NULL; transmit_lookup_dns_result (rh); return; } @@ -1504,6 +729,16 @@ recursive_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +/** + * Begin the resolution process from 'name', starting with + * the identification of the zone specified by 'name'. + * + * @param rh resolution to perform + */ +static void +start_resolver_lookup (struct GNS_ResolverHandle *rh); + + /** * Function called with the result of a DNS resolution. * @@ -1511,7 +746,7 @@ recursive_resolution (void *cls, * we were attempting to make * @param rs socket that received the response * @param dns dns response, never NULL - * @param dns_len number of bytes in 'dns' + * @param dns_len number of bytes in @a dns */ static void dns_result_parser (void *cls, @@ -1521,11 +756,14 @@ dns_result_parser (void *cls, { struct GNS_ResolverHandle *rh = cls; struct GNUNET_DNSPARSER_Packet *p; + const struct GNUNET_DNSPARSER_Record *rec; + unsigned int rd_count; + unsigned int i; rh->dns_request = NULL; GNUNET_SCHEDULER_cancel (rh->task_id); rh->task_id = GNUNET_SCHEDULER_NO_TASK; - p = GNUNET_DNSPARSER_parse ((const char *) dns, + p = GNUNET_DNSPARSER_parse ((const char *) dns, dns_len); if (NULL == p) { @@ -1535,17 +773,156 @@ dns_result_parser (void *cls, GNS_resolver_lookup_cancel (rh); return; } - // FIXME: - // Check if the packet is the final answer, or - // just pointing us to another NS or another name (CNAME), or another domain (DNAME); - // then do the right thing (TM) -- possibly using "recursive_dns_resolution". - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("NOT IMPLEMENTED\n")); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DNS response for `%s' with %u answers\n", + rh->ac_tail->label, + (unsigned int) p->num_answers); + if ( (p->num_answers > 0) && + (GNUNET_DNSPARSER_TYPE_CNAME == p->answers[0].type) && + (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type) ) + { + GNUNET_free (rh->name); + rh->name = GNUNET_strdup (p->answers[0].data.hostname); + start_resolver_lookup (rh); + GNUNET_DNSPARSER_free_packet (p); + return; + } + /* FIXME: add DNAME support */ - + /* convert from (parsed) DNS to (binary) GNS format! */ + rd_count = p->num_answers + p->num_authority_records + p->num_additional_records; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + unsigned int skip; + char buf[UINT16_MAX]; + size_t buf_off; + size_t buf_start; + + buf_off = 0; + skip = 0; + memset (rd, 0, sizeof (rd)); + for (i=0;inum_answers) + rec = &p->answers[i]; + else if (i < p->num_answers + p->num_authority_records) + rec = &p->authority_records[i - p->num_answers]; + else + rec = &p->authority_records[i - p->num_answers - p->num_authority_records]; + /* As we copied the full DNS name to 'rh->ac_tail->label', this + should be the correct check to see if this record is actually + a record for our label... */ + if (0 != strcmp (rec->name, + rh->ac_tail->label)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dropping record `%s', does not match desired name `%s'\n", + rec->name, + rh->ac_tail->label); + skip++; + continue; + } + rd[i - skip].record_type = rec->type; + rd[i - skip].expiration_time = rec->expiration_time.abs_value_us; + switch (rec->type) + { + case GNUNET_DNSPARSER_TYPE_A: + if (rec->data.raw.data_len != sizeof (struct in_addr)) + { + GNUNET_break_op (0); + skip++; + continue; + } + rd[i - skip].data_size = rec->data.raw.data_len; + rd[i - skip].data = rec->data.raw.data; + break; + case GNUNET_DNSPARSER_TYPE_AAAA: + if (rec->data.raw.data_len != sizeof (struct in6_addr)) + { + GNUNET_break_op (0); + skip++; + continue; + } + rd[i - skip].data_size = rec->data.raw.data_len; + rd[i - skip].data = rec->data.raw.data; + break; + case GNUNET_DNSPARSER_TYPE_CNAME: + case GNUNET_DNSPARSER_TYPE_PTR: + case GNUNET_DNSPARSER_TYPE_NS: + buf_start = buf_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_name (buf, + sizeof (buf), + &buf_off, + rec->data.hostname)) + { + GNUNET_break (0); + skip++; + continue; + } + rd[i - skip].data_size = buf_off - buf_start; + rd[i - skip].data = &buf[buf_start]; + break; + case GNUNET_DNSPARSER_TYPE_SOA: + buf_start = buf_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_soa (buf, + sizeof (buf), + &buf_off, + rec->data.soa)) + { + GNUNET_break (0); + skip++; + continue; + } + rd[i - skip].data_size = buf_off - buf_start; + rd[i - skip].data = &buf[buf_start]; + break; + case GNUNET_DNSPARSER_TYPE_MX: + buf_start = buf_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_mx (buf, + sizeof (buf), + &buf_off, + rec->data.mx)) + { + GNUNET_break (0); + skip++; + continue; + } + rd[i - skip].data_size = buf_off - buf_start; + rd[i - skip].data = &buf[buf_start]; + break; + case GNUNET_DNSPARSER_TYPE_SRV: + buf_start = buf_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_srv (buf, + sizeof (buf), + &buf_off, + rec->data.srv)) + { + GNUNET_break (0); + skip++; + continue; + } + rd[i - skip].data_size = buf_off - buf_start; + rd[i - skip].data = &buf[buf_start]; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Skipping record of unsupported type %d\n"), + rec->type); + skip++; + continue; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returning DNS response for `%s' with %u answers\n", + rh->ac_tail->label, + (unsigned int) p->num_answers); + rh->proc (rh->proc_cls, rd_count - skip, rd); + GNS_resolver_lookup_cancel (rh); + } GNUNET_DNSPARSER_free_packet (p); } @@ -1570,6 +947,9 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) ac = rh->ac_tail; GNUNET_assert (NULL != ac); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting DNS lookup for `%s'\n", + ac->label); GNUNET_assert (GNUNET_NO == ac->gns_authority); switch (((const struct sockaddr *) &ac->authority_info.dns_authority.dns_ip)->sa_family) { @@ -1588,7 +968,7 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) query = GNUNET_new (struct GNUNET_DNSPARSER_Query); query->name = GNUNET_strdup (ac->label); query->type = rh->record_type; - query->class = GNUNET_TUN_DNS_CLASS_INTERNET; + query->dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; p = GNUNET_new (struct GNUNET_DNSPARSER_Packet); p->queries = query; p->num_queries = 1; @@ -1596,7 +976,7 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) UINT16_MAX); p->flags.opcode = GNUNET_TUN_DNS_OPCODE_QUERY; p->flags.recursion_desired = 1; - if (GNUNET_OK != + if (GNUNET_OK != GNUNET_DNSPARSER_pack (p, 1024, &dns_request, &dns_request_length)) { GNUNET_break (0); @@ -1625,32 +1005,156 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) * We encountered a CNAME record during our resolution. * Merge it into our chain. * - * @param rh resolution we are performing - * @param cname value of the cname record we got for the current - * authority chain tail + * @param rh resolution we are performing + * @param cname value of the cname record we got for the current + * authority chain tail + */ +static void +handle_gns_cname_result (struct GNS_ResolverHandle *rh, + const char *cname) +{ + size_t nlen; + char *res; + struct AuthorityChain *ac; + + nlen = strlen (cname); + if ( (nlen > 2) && + (0 == strcmp (".+", + &cname[nlen - 2])) ) + { + /* CNAME resolution continues relative to current domain */ + if (0 == rh->name_resolution_pos) + { + res = GNUNET_strndup (cname, nlen - 2); + rh->name_resolution_pos = nlen - 2; + } + else + { + GNUNET_asprintf (&res, + "%.*s.%.*s", + (int) rh->name_resolution_pos, + rh->name, + (int) (nlen - 2), + cname); + rh->name_resolution_pos = strlen (res); + } + GNUNET_free (rh->name); + rh->name = res; + ac = GNUNET_new (struct AuthorityChain); + ac->rh = rh; + ac->gns_authority = GNUNET_YES; + ac->authority_info.gns_authority = rh->ac_tail->authority_info.gns_authority; + ac->label = resolver_lookup_get_next_label (rh); + /* tigger shortening */ + if (NULL != rh->shorten_key) + GNS_shorten_start (rh->ac_tail->label, + &ac->authority_info.gns_authority, + rh->shorten_key); + /* add AC to tail */ + GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, + rh->ac_tail, + ac); + rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, + rh); + return; + } + /* name is absolute, start from the beginning */ + GNUNET_free (rh->name); + rh->name = GNUNET_strdup (cname); + start_resolver_lookup (rh); +} + + +/** + * Process a records that were decrypted from a block. + * + * @param cls closure with the 'struct GNS_ResolverHandle' + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +handle_gns_resolution_result (void *cls, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd); + + +/** + * Callback invoked from the VPN service once a redirection is + * available. Provides the IP address that can now be used to + * reach the requested destination. Replaces the "VPN" record + * with the respective A/AAAA record and continues processing. + * + * @param cls closure + * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error; + * will match 'result_af' from the request + * @param address IP address (struct in_addr or struct in_addr6, depending on 'af') + * that the VPN allocated for the redirection; + * traffic to this IP will now be redirected to the + * specified target peer; NULL on error */ static void -handle_gns_cname_result (struct GNS_ResolverHandle *rh, - const char *cname) +vpn_allocation_cb (void *cls, + int af, + const void *address) { - // FIXME: not implemented - GNUNET_break (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + struct VpnContext *vpn_ctx = cls; + struct GNS_ResolverHandle *rh = vpn_ctx->rh; + struct GNUNET_GNSRECORD_Data rd[vpn_ctx->rd_count]; + unsigned int i; + + vpn_ctx->vpn_request = NULL; + rh->vpn_ctx = NULL; + GNUNET_assert (GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (vpn_ctx->rd_data_size, + vpn_ctx->rd_data, + vpn_ctx->rd_count, + rd)); + for (i=0;ird_count;i++) + { + if (GNUNET_GNSRECORD_TYPE_VPN == rd[i].record_type) + { + switch (af) + { + case AF_INET: + rd[i].record_type = GNUNET_DNSPARSER_TYPE_A; + rd[i].data_size = sizeof (struct in_addr); + rd[i].expiration_time = GNUNET_TIME_relative_to_absolute (VPN_TIMEOUT).abs_value_us; + rd[i].flags = 0; + rd[i].data = address; + break; + case AF_INET6: + rd[i].record_type = GNUNET_DNSPARSER_TYPE_AAAA; + rd[i].expiration_time = GNUNET_TIME_relative_to_absolute (VPN_TIMEOUT).abs_value_us; + rd[i].flags = 0; + rd[i].data = address; + rd[i].data_size = sizeof (struct in6_addr); + break; + default: + GNUNET_assert (0); + } + break; + } + } + GNUNET_assert (i < vpn_ctx->rd_count); + handle_gns_resolution_result (rh, + vpn_ctx->rd_count, + rd); + GNUNET_free (vpn_ctx->rd_data); + GNUNET_free (vpn_ctx); } /** * Process a records that were decrypted from a block. * - * @param cls closure with the 'struct GNS_ResolverHandle' + * @param cls closure with the `struct GNS_ResolverHandle` * @param rd_count number of entries in @a rd array * @param rd array of records with data to store */ static void handle_gns_resolution_result (void *cls, unsigned int rd_count, - const struct GNUNET_NAMESTORE_RecordData *rd) + const struct GNUNET_GNSRECORD_Data *rd) { struct GNS_ResolverHandle *rh = cls; struct AuthorityChain *ac; @@ -1661,7 +1165,23 @@ handle_gns_resolution_result (void *cls, struct sockaddr_in6 v6; size_t sa_len; char *cname; - + struct VpnContext *vpn_ctx; + const struct GNUNET_TUN_GnsVpnRecord *vpn; + const char *vname; + struct GNUNET_HashCode vhash; + int af; + char scratch[UINT16_MAX]; + size_t scratch_off; + size_t scratch_start; + size_t off; + struct GNUNET_GNSRECORD_Data rd_new[rd_count]; + unsigned int rd_off; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolution succeeded for `%s' in zone %s, got %u records\n", + rh->ac_tail->label, + GNUNET_GNSRECORD_z2s (&rh->ac_tail->authority_info.gns_authority), + rd_count); if (0 == rh->name_resolution_pos) { /* top-level match, are we done yet? */ @@ -1669,35 +1189,325 @@ handle_gns_resolution_result (void *cls, (GNUNET_DNSPARSER_TYPE_CNAME == rd[0].record_type) && (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type) ) { - cname = GNUNET_strndup (rd[0].data, - rd[0].data_size); - handle_gns_cname_result (rh, + off = 0; + cname = GNUNET_DNSPARSER_parse_name (rd[0].data, + rd[0].data_size, + &off); + if ( (NULL == cname) || + (off != rd[0].data_size) ) + { + GNUNET_break_op (0); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + GNUNET_free_non_null (cname); + return; + } + handle_gns_cname_result (rh, cname); GNUNET_free (cname); - return; + return; + } + /* If A/AAAA was requested, but we got a VPN + record, we convert it to A/AAAA using GNUnet VPN */ + if ( (GNUNET_DNSPARSER_TYPE_A == rh->record_type) || + (GNUNET_DNSPARSER_TYPE_AAAA == rh->record_type) ) + { + for (i=0;irecord_type) ? AF_INET : AF_INET6; + if (sizeof (struct GNUNET_TUN_GnsVpnRecord) < + rd[i].data_size) + { + GNUNET_break_op (0); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + vpn = (const struct GNUNET_TUN_GnsVpnRecord *) rd[i].data; + vname = (const char *) &vpn[1]; + if ('\0' != vname[rd[i].data_size - 1 - sizeof (struct GNUNET_TUN_GnsVpnRecord)]) + { + GNUNET_break_op (0); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + GNUNET_CRYPTO_hash (vname, + strlen (vname), // FIXME: +1? + &vhash); + vpn_ctx = GNUNET_new (struct VpnContext); + rh->vpn_ctx = vpn_ctx; + vpn_ctx->rh = rh; + vpn_ctx->rd_data_size = GNUNET_GNSRECORD_records_get_size (rd_count, + rd); + vpn_ctx->rd_data = GNUNET_malloc (vpn_ctx->rd_data_size); + (void) GNUNET_GNSRECORD_records_serialize (rd_count, + rd, + vpn_ctx->rd_data_size, + vpn_ctx->rd_data); + vpn_ctx->vpn_request = GNUNET_VPN_redirect_to_peer (vpn_handle, + af, + ntohs (vpn->proto), + &vpn->peer, + &vhash, + GNUNET_TIME_relative_to_absolute (VPN_TIMEOUT), + &vpn_allocation_cb, + rh); + return; + } + case GNUNET_GNSRECORD_TYPE_GNS2DNS: + { + /* delegation to DNS */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found GNS2DNS record, delegating to DNS!\n"); + goto do_recurse; + } + default: + break; + } + } + } + /* convert relative names in record values to absolute names, + using 'scratch' array for memory allocations */ + scratch_off = 0; + rd_off = 0; + for (i=0;imname = translate_dot_plus (rh, soa->mname); + soa->rname = translate_dot_plus (rh, soa->rname); + scratch_start = scratch_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_soa (scratch, + sizeof (scratch), + &scratch_off, + soa)) + { + GNUNET_break (0); + } + else + { + rd_new[rd_off].data = &scratch[scratch_start]; + rd_new[rd_off].data_size = scratch_off - scratch_start; + rd_off++; + } + } + if (NULL != soa) + GNUNET_DNSPARSER_free_soa (soa); + } + break; + case GNUNET_DNSPARSER_TYPE_MX: + { + struct GNUNET_DNSPARSER_MxRecord *mx; + + off = 0; + mx = GNUNET_DNSPARSER_parse_mx (rd[i].data, + rd[i].data_size, + &off); + if ( (NULL == mx) || + (off != rd[i].data_size) ) + { + GNUNET_break_op (0); /* record not well-formed */ + } + else + { + mx->mxhost = translate_dot_plus (rh, mx->mxhost); + scratch_start = scratch_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_mx (scratch, + sizeof (scratch), + &scratch_off, + mx)) + { + GNUNET_break (0); + } + else + { + rd_new[rd_off].data = &scratch[scratch_start]; + rd_new[rd_off].data_size = scratch_off - scratch_start; + rd_off++; + } + } + if (NULL != mx) + GNUNET_DNSPARSER_free_mx (mx); + } + break; + case GNUNET_DNSPARSER_TYPE_SRV: + { + struct GNUNET_DNSPARSER_SrvRecord *srv; + + off = 0; + /* FIXME: passing rh->name here is is not necessarily what we want + (SRV support not finished) */ + srv = GNUNET_DNSPARSER_parse_srv (rh->name, + rd[i].data, + rd[i].data_size, + &off); + if ( (NULL == srv) || + (off != rd[i].data_size) ) + { + GNUNET_break_op (0); /* record not well-formed */ + } + else + { + srv->domain_name = translate_dot_plus (rh, srv->domain_name); + srv->target = translate_dot_plus (rh, srv->target); + scratch_start = scratch_off; + if (GNUNET_OK != + GNUNET_DNSPARSER_builder_add_srv (scratch, + sizeof (scratch), + &scratch_off, + srv)) + { + GNUNET_break (0); + } + else + { + rd_new[rd_off].data = &scratch[scratch_start]; + rd_new[rd_off].data_size = scratch_off - scratch_start; + rd_off++; + } + } + if (NULL != srv) + GNUNET_DNSPARSER_free_srv (srv); + } + break; + case GNUNET_GNSRECORD_TYPE_PKEY: + { + struct GNUNET_CRYPTO_EcdsaPublicKey pub; + + if (rd[i].data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) + { + GNUNET_break_op (0); + break; + } + memcpy (&pub, rd[i].data, rd[i].data_size); + + /* tigger shortening */ + if (NULL != rh->shorten_key) + { + GNS_shorten_start (rh->ac_tail->label, + &pub, + rh->shorten_key); + } + rd_off++; + if (GNUNET_GNSRECORD_TYPE_PKEY != rh->record_type) + { + /* try to resolve "+" */ + struct AuthorityChain *ac; + + ac = GNUNET_new (struct AuthorityChain); + ac->rh = rh; + ac->gns_authority = GNUNET_YES; + ac->authority_info.gns_authority = pub; + ac->label = GNUNET_strdup (GNUNET_GNS_MASTERZONE_STR); + GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, + rh->ac_tail, + ac); + rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, + rh); + return; + } + } + break; + case GNUNET_GNSRECORD_TYPE_GNS2DNS: + { + /* delegation to DNS */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found GNS2DNS record, delegating to DNS!\n"); + goto do_recurse; + } + default: + rd_off++; + break; + } } - /* FIXME: if A/AAAA was requested, but we got a VPN - record, we should interact with GNUnet VPN here */ - + /* yes, we are done, return result */ - rh->proc (rh->proc_cls, rd_count, rd); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returning GNS response for `%s' with %u answers\n", + rh->ac_tail->label, + rd_off); + rh->proc (rh->proc_cls, rd_off, rd_new); GNS_resolver_lookup_cancel (rh); - return; + return; } + do_recurse: /* need to recurse, check if we can */ for (i=0;iproc (rh->proc_cls, 0, NULL); GNS_resolver_lookup_cancel (rh); - return; + return; } /* expand authority chain */ ac = GNUNET_new (struct AuthorityChain); @@ -1705,119 +1515,167 @@ handle_gns_resolution_result (void *cls, ac->gns_authority = GNUNET_YES; memcpy (&ac->authority_info.gns_authority, rd[i].data, - sizeof (struct GNUNET_CRYPTO_EccPublicKey)); + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); ac->label = resolver_lookup_get_next_label (rh); + /* tigger shortening */ + if (NULL != rh->shorten_key) + GNS_shorten_start (rh->ac_tail->label, + &ac->authority_info.gns_authority, + rh->shorten_key); + /* add AC to tail */ GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, rh->ac_tail, ac); /* recurse */ rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, rh); - break; - case GNUNET_DNSPARSER_TYPE_NS: - /* resolution continues within DNS */ - if (GNUNET_DNSPARSER_MAX_NAME_LENGTH < rd[i].data_size) - { - GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* find associated A/AAAA record */ - sa = NULL; - for (j=0;jproc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* FIXME: might want to check if we support IPv4 here, - and otherwise skip this one and hope we find another */ - memset (&v4, 0, sizeof (v4)); - sa_len = sizeof (v4); - v4.sin_family = AF_INET; - v4.sin_port = htons (53); + GNUNET_break_op (0); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + /* find associated A/AAAA record */ + sa = NULL; + sa_len = 0; + for (j=0;jproc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + /* FIXME: might want to check if we support IPv4 here, + and otherwise skip this one and hope we find another */ + memset (&v4, 0, sizeof (v4)); + sa_len = sizeof (v4); + v4.sin_family = AF_INET; + v4.sin_port = htons (53); #if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = (u_char) sa_len; + v4.sin_len = (u_char) sa_len; #endif - memcpy (&v4.sin_addr, - rd[j].data, - sizeof (struct in_addr)); - sa = (struct sockaddr *) &v4; - break; - case GNUNET_DNSPARSER_TYPE_AAAA: - if (sizeof (struct in6_addr) != rd[i].data_size) - { - GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* FIXME: might want to check if we support IPv6 here, - and otherwise skip this one and hope we find another */ - memset (&v6, 0, sizeof (v6)); - sa_len = sizeof (v6); - v6.sin6_family = AF_INET6; - v6.sin6_port = htons (53); + memcpy (&v4.sin_addr, + rd[j].data, + sizeof (struct in_addr)); + sa = (struct sockaddr *) &v4; + break; + case GNUNET_DNSPARSER_TYPE_AAAA: + if (sizeof (struct in6_addr) != rd[j].data_size) + { + GNUNET_break_op (0); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + /* FIXME: might want to check if we support IPv6 here, + and otherwise skip this one and hope we find another */ + memset (&v6, 0, sizeof (v6)); + sa_len = sizeof (v6); + v6.sin6_family = AF_INET6; + v6.sin6_port = htons (53); #if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = (u_char) sa_len; + v6.sin6_len = (u_char) sa_len; #endif - memcpy (&v6.sin6_addr, - rd[j].data, - sizeof (struct in6_addr)); - sa = (struct sockaddr *) &v6; - break; + memcpy (&v6.sin6_addr, + rd[j].data, + sizeof (struct in6_addr)); + sa = (struct sockaddr *) &v6; + break; + default: + break; + } + if (NULL != sa) + break; } - if (NULL != sa) - break; + if (NULL == sa) + { + /* we cannot continue; NS without A/AAAA */ + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + /* expand authority chain */ + ac = GNUNET_new (struct AuthorityChain); + ac->rh = rh; + off = 0; + ns = GNUNET_DNSPARSER_parse_name (rd[i].data, + rd[i].data_size, + &off); + if ( (NULL == ns) || + (off != rd[i].data_size) ) + { + GNUNET_break_op (0); /* record not well-formed */ + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + GNUNET_free_non_null (ns); + GNUNET_free (ac); + return; + } + strcpy (ac->authority_info.dns_authority.name, + ns); + memcpy (&ac->authority_info.dns_authority.dns_ip, + sa, + sa_len); + /* for DNS recursion, the label is the full DNS name, + created from the remainder of the GNS name and the + name in the NS record */ + GNUNET_asprintf (&ac->label, + "%.*s%s%s", + (int) rh->name_resolution_pos, + rh->name, + (0 != rh->name_resolution_pos) ? "." : "", + ns); + GNUNET_free (ns); + GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, + rh->ac_tail, + ac); + if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("GNS lookup resulted in DNS name that is too long (`%s')\n"), + ac->label); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + /* recurse */ + rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, + rh); + return; } - /* expand authority chain */ - ac = GNUNET_new (struct AuthorityChain); - ac->rh = rh; - strncpy (ac->authority_info.dns_authority.name, - rd[i].data, - rd[i].data_size); - ac->authority_info.dns_authority.name[rd[i].data_size] = '\0'; - memcpy (&ac->authority_info.dns_authority.dns_ip, - sa, - sa_len); - /* for DNS recursion, the label is the full DNS name, - created from the remainder of the GNS name and the - name in the NS record */ - GNUNET_asprintf (&ac->label, - "%.*s%s", - (int) rh->name_resolution_pos, - rh->name, - ac->authority_info.dns_authority.name); - GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, - rh->ac_tail, - ac); - if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) + case GNUNET_DNSPARSER_TYPE_CNAME: { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("GNS lookup resulted in DNS name that is too long (`%s')\n"), - ac->label); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + char *cname; + + off = 0; + cname = GNUNET_DNSPARSER_parse_name (rd[i].data, + rd[i].data_size, + &off); + if ( (NULL == cname) || + (off != rd[i].data_size) ) + { + GNUNET_break_op (0); /* record not well-formed */ + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + GNUNET_free_non_null (cname); + return; + } + handle_gns_cname_result (rh, + cname); + GNUNET_free (cname); return; } - /* recurse */ - rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, - rh); - return; - case GNUNET_DNSPARSER_TYPE_CNAME: - cname = GNUNET_strndup (rd[i].data, - rd[i].data_size); - handle_gns_cname_result (rh, - cname); - GNUNET_free (cname); - return; /* FIXME: handle DNAME */ default: /* skip */ @@ -1835,22 +1693,26 @@ handle_gns_resolution_result (void *cls, * Function called once the namestore has completed the request for * caching a block. * - * @param cls closure with the 'struct GNS_ResolverHandle' + * @param cls closure with the `struct CacheOps` * @param success #GNUNET_OK on success * @param emsg error message */ static void -namestore_cache_continuation (void *cls, +namecache_cache_continuation (void *cls, int32_t success, const char *emsg) { - struct GNS_ResolverHandle *rh = cls; + struct CacheOps *co = cls; - rh->namestore_qe = NULL; + co->namecache_qe_cache = NULL; if (NULL != emsg) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to cache GNS resolution: %s\n"), emsg); + GNUNET_CONTAINER_DLL_remove (co_head, + co_tail, + co); + GNUNET_free (co); } @@ -1858,15 +1720,15 @@ namestore_cache_continuation (void *cls, * Iterator called on each result obtained for a DHT * operation that expects a reply * - * @param cls closure with the 'struct GNS_ResolverHandle' + * @param cls closure with the `struct GNS_ResolverHandle` * @param exp when will this value expire * @param key key of the result * @param get_path peers on reply path (or NULL if not recorded) * [0] = datastore's first neighbor, [length - 1] = local peer - * @param get_path_length number of entries in get_path + * @param get_path_length number of entries in @a get_path * @param put_path peers on the PUT path (or NULL if not recorded) * [0] = origin, [length - 1] = datastore - * @param put_path_length number of entries in get_path + * @param put_path_length number of entries in @a put_path * @param type type of the result * @param size number of bytes in data * @param data pointer to the result data @@ -1874,44 +1736,47 @@ namestore_cache_continuation (void *cls, static void handle_dht_response (void *cls, struct GNUNET_TIME_Absolute exp, - const struct GNUNET_HashCode * key, + const struct GNUNET_HashCode *key, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, - const struct GNUNET_PeerIdentity *put_path, + const struct GNUNET_PeerIdentity *put_path, unsigned int put_path_length, enum GNUNET_BLOCK_Type type, size_t size, const void *data) { struct GNS_ResolverHandle *rh = cls; struct AuthorityChain *ac = rh->ac_tail; - const struct GNUNET_NAMESTORE_Block *block; - + const struct GNUNET_GNSRECORD_Block *block; + struct CacheOps *co; + GNUNET_DHT_get_stop (rh->get_handle); rh->get_handle = NULL; GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node); - rh->dht_heap_node = NULL; - if (size < sizeof (struct GNUNET_NAMESTORE_Block)) + rh->dht_heap_node = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling response from the DHT\n"); + if (size < sizeof (struct GNUNET_GNSRECORD_Block)) { /* how did this pass DHT block validation!? */ GNUNET_break (0); rh->proc (rh->proc_cls, 0, NULL); GNS_resolver_lookup_cancel (rh); - return; + return; } - block = data; + block = data; if (size != - ntohs (block->purpose.size) + - sizeof (struct GNUNET_CRYPTO_EccPublicKey) + - sizeof (struct GNUNET_CRYPTO_EccSignature)) + ntohl (block->purpose.size) + + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + + sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) { /* how did this pass DHT block validation!? */ GNUNET_break (0); rh->proc (rh->proc_cls, 0, NULL); GNS_resolver_lookup_cancel (rh); - return; + return; } if (GNUNET_OK != - GNUNET_NAMESTORE_block_decrypt (block, + GNUNET_GNSRECORD_block_decrypt (block, &ac->authority_info.gns_authority, ac->label, &handle_gns_resolution_result, @@ -1922,38 +1787,58 @@ handle_dht_response (void *cls, GNS_resolver_lookup_cancel (rh); return; } + if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received expired block from the DHT, will not cache it.\n"); + return; + } /* Cache well-formed blocks */ - rh->namestore_qe = GNUNET_NAMESTORE_block_cache (namestore_handle, - block, - &namestore_cache_continuation, - rh); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Caching response from the DHT in namestore\n"); + co = GNUNET_new (struct CacheOps); + co->namecache_qe_cache = GNUNET_NAMECACHE_block_cache (namecache_handle, + block, + &namecache_cache_continuation, + co); + GNUNET_CONTAINER_DLL_insert (co_head, + co_tail, + co); } /** * Process a record that was stored in the namestore. * - * @param cls closure with the 'struct GNS_ResolverHandle' + * @param cls closure with the `struct GNS_ResolverHandle` * @param block block that was stored in the namestore */ -static void +static void handle_namestore_block_response (void *cls, - const struct GNUNET_NAMESTORE_Block *block) + const struct GNUNET_GNSRECORD_Block *block) { struct GNS_ResolverHandle *rh = cls; struct GNS_ResolverHandle *rx; struct AuthorityChain *ac = rh->ac_tail; const char *label = ac->label; - const struct GNUNET_CRYPTO_EccPublicKey *auth = &ac->authority_info.gns_authority; + const struct GNUNET_CRYPTO_EcdsaPublicKey *auth = &ac->authority_info.gns_authority; struct GNUNET_HashCode query; - GNUNET_NAMESTORE_query_from_public_key (auth, + GNUNET_GNSRECORD_query_from_public_key (auth, label, &query); - rh->namestore_qe = NULL; - if (NULL == block) + GNUNET_assert (NULL != rh->namecache_qe); + rh->namecache_qe = NULL; + if ( (GNUNET_NO == rh->only_cached) && + ( (NULL == block) || + (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) ) ) { /* Namestore knows nothing; try DHT lookup */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting DHT lookup for `%s' in zone %s\n", + ac->label, + GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); + GNUNET_assert (NULL == rh->get_handle); rh->get_handle = GNUNET_DHT_get_start (dht_handle, GNUNET_BLOCK_TYPE_GNS_NAMERECORD, &query, @@ -1968,13 +1853,26 @@ handle_namestore_block_response (void *cls, { /* fail longest-standing DHT request */ rx = GNUNET_CONTAINER_heap_peek (dht_lookup_heap); + GNUNET_assert (NULL != rx); rx->proc (rx->proc_cls, 0, NULL); GNS_resolver_lookup_cancel (rx); } return; } + if ( (NULL == block) || + (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) ) + { + /* DHT not permitted and no local result, fail */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolution failed for `%s' in zone %s (DHT lookup not permitted by configuration)\n", + ac->label, + GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } if (GNUNET_OK != - GNUNET_NAMESTORE_block_decrypt (block, + GNUNET_GNSRECORD_block_decrypt (block, auth, label, &handle_gns_resolution_result, @@ -1999,20 +1897,73 @@ recursive_gns_resolution_namestore (struct GNS_ResolverHandle *rh) struct AuthorityChain *ac = rh->ac_tail; struct GNUNET_HashCode query; - GNUNET_NAMESTORE_query_from_public_key (&ac->authority_info.gns_authority, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting GNS resolution for `%s' in zone %s\n", + ac->label, + GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); + GNUNET_GNSRECORD_query_from_public_key (&ac->authority_info.gns_authority, ac->label, &query); - rh->namestore_qe = GNUNET_NAMESTORE_lookup_block (namestore_handle, + rh->namecache_qe = GNUNET_NAMECACHE_lookup_block (namecache_handle, &query, &handle_namestore_block_response, rh); + GNUNET_assert (NULL != rh->namecache_qe); +} + + +/** + * Function called with the result from a revocation check. + * + * @param cls the `struct GNS_ResovlerHandle` + * @param is_valid #GNUNET_YES if the zone was not yet revoked + */ +static void +handle_revocation_result (void *cls, + int is_valid) +{ + struct GNS_ResolverHandle *rh = cls; + struct AuthorityChain *ac = rh->ac_tail; + + rh->rev_check = NULL; + if (GNUNET_YES != is_valid) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Zone %s was revoked, resolution fails\n"), + GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); + rh->proc (rh->proc_cls, 0, NULL); + GNS_resolver_lookup_cancel (rh); + return; + } + recursive_gns_resolution_namestore (rh); +} + + +/** + * Perform revocation check on tail of our authority chain. + * + * @param rh query we are processing + */ +static void +recursive_gns_resolution_revocation (struct GNS_ResolverHandle *rh) +{ + struct AuthorityChain *ac = rh->ac_tail; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting revocation check for zone %s\n", + GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); + rh->rev_check = GNUNET_REVOCATION_query (cfg, + &ac->authority_info.gns_authority, + &handle_revocation_result, + rh); + GNUNET_assert (NULL != rh->rev_check); } /** * Task scheduled to continue with the resolution process. * - * @param cls the 'struct GNS_ResolverHandle' of the resolution + * @param cls the `struct GNS_ResolverHandle` of the resolution * @param tc task context */ static void @@ -2032,65 +1983,33 @@ recursive_resolution (void *cls, return; } if (GNUNET_YES == rh->ac_tail->gns_authority) - recursive_gns_resolution_namestore (rh); + recursive_gns_resolution_revocation (rh); else recursive_dns_resolution (rh); } /** - * Lookup of a record in a specific zone calls lookup result processor - * on result. + * Begin the resolution process from 'name', starting with + * the identification of the zone specified by 'name'. * - * @param zone the zone to perform the lookup in - * @param record_type the record type to look up - * @param name the name to look up - * @param shorten_key a private key for use with PSEU import (can be NULL) - * @param only_cached GNUNET_NO to only check locally not DHT for performance - * @param proc the processor to call on result - * @param proc_cls the closure to pass to @a proc - * @return handle to cancel operation + * @param rh resolution to perform */ -struct GNS_ResolverHandle * -GNS_resolver_lookup (const struct GNUNET_CRYPTO_EccPublicKey *zone, - uint32_t record_type, - const char *name, - const struct GNUNET_CRYPTO_EccPrivateKey *shorten_key, - int only_cached, - GNS_ResultProcessor proc, void *proc_cls) +static void +start_resolver_lookup (struct GNS_ResolverHandle *rh) { - struct GNS_ResolverHandle *rh; struct AuthorityChain *ac; - char *x; char *y; - char *pkey; - - rh = GNUNET_new (struct GNS_ResolverHandle); - GNUNET_CONTAINER_DLL_insert (rlh_head, - rlh_tail, - rh); - rh->authority_zone = *zone; - rh->proc = proc; - rh->proc_cls = proc_cls; - rh->only_cached = only_cached; - rh->record_type = record_type; - rh->name = GNUNET_strdup (name); - rh->name_resolution_pos = strlen (name); - if (NULL != shorten_key) - { - rh->shorten_key = GNUNET_new (struct GNUNET_CRYPTO_EccPrivateKey); - *rh->shorten_key = *shorten_key; - } - if ( ( (GNUNET_YES == is_canonical (name)) && - (0 != strcmp (GNUNET_GNS_TLD, name)) ) || - ( (GNUNET_YES != is_gnu_tld (name)) && - (GNUNET_YES != is_zkey_tld (name)) ) ) + if ( ( (GNUNET_YES == is_canonical (rh->name)) && + (0 != strcmp (GNUNET_GNS_TLD, rh->name)) ) || + ( (GNUNET_YES != is_gnu_tld (rh->name)) && + (GNUNET_YES != is_zkey_tld (rh->name)) ) ) { /* use standard DNS lookup */ int af; - switch (record_type) + switch (rh->record_type) { case GNUNET_DNSPARSER_TYPE_A: af = AF_INET; @@ -2102,38 +2021,34 @@ GNS_resolver_lookup (const struct GNUNET_CRYPTO_EccPublicKey *zone, af = AF_UNSPEC; break; } - rh->std_resolve = GNUNET_RESOLVER_ip_get (name, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Doing standard DNS lookup for `%s'\n", + rh->name); + rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name, af, DNS_LOOKUP_TIMEOUT, &handle_dns_result, rh); - return rh; + return; } - if (is_zkey_tld (name)) + if (is_zkey_tld (rh->name)) { /* Name ends with ".zkey", try to replace authority zone with zkey authority */ GNUNET_free (resolver_lookup_get_next_label (rh)); /* will return "zkey" */ - x = resolver_lookup_get_next_label (rh); /* will return 'x' coordinate */ y = resolver_lookup_get_next_label (rh); /* will return 'y' coordinate */ - GNUNET_asprintf (&pkey, - "%s%s", - x, y); - if ( (NULL == x) || - (NULL == y) || + if ( (NULL == y) || (GNUNET_OK != - GNUNET_CRYPTO_ecc_public_key_from_string (pkey, - strlen (pkey), - &rh->authority_zone)) ) + GNUNET_CRYPTO_ecdsa_public_key_from_string (y, + strlen (y), + &rh->authority_zone)) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Hostname `%s' is not well-formed, resolution fails\n"), - name); + rh->name); rh->task_id = GNUNET_SCHEDULER_add_now (&fail_resolution, rh); } - GNUNET_free_non_null (x); GNUNET_free_non_null (y); - GNUNET_free (pkey); } else { @@ -2153,6 +2068,54 @@ GNS_resolver_lookup (const struct GNUNET_CRYPTO_EccPublicKey *zone, ac); rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, rh); +} + + +/** + * Lookup of a record in a specific zone calls lookup result processor + * on result. + * + * @param zone the zone to perform the lookup in + * @param record_type the record type to look up + * @param name the name to look up + * @param shorten_key a private key for use with PSEU import (can be NULL) + * @param only_cached #GNUNET_NO to only check locally not DHT for performance + * @param proc the processor to call on result + * @param proc_cls the closure to pass to @a proc + * @return handle to cancel operation + */ +struct GNS_ResolverHandle * +GNS_resolver_lookup (const struct GNUNET_CRYPTO_EcdsaPublicKey *zone, + uint32_t record_type, + const char *name, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key, + int only_cached, + GNS_ResultProcessor proc, void *proc_cls) +{ + struct GNS_ResolverHandle *rh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + (NULL == shorten_key) + ? "Starting lookup for `%s' with shortening disabled\n" + : "Starting lookup for `%s' with shortening enabled\n", + name); + rh = GNUNET_new (struct GNS_ResolverHandle); + GNUNET_CONTAINER_DLL_insert (rlh_head, + rlh_tail, + rh); + rh->authority_zone = *zone; + rh->proc = proc; + rh->proc_cls = proc_cls; + rh->only_cached = only_cached; + rh->record_type = record_type; + rh->name = GNUNET_strdup (name); + rh->name_resolution_pos = strlen (name); + if (NULL != shorten_key) + { + rh->shorten_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); + *rh->shorten_key = *shorten_key; + } + start_resolver_lookup (rh); return rh; } @@ -2167,6 +2130,7 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) { struct DnsResult *dr; struct AuthorityChain *ac; + struct VpnContext *vpn_ctx; GNUNET_CONTAINER_DLL_remove (rlh_head, rlh_tail, @@ -2194,18 +2158,31 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node); rh->dht_heap_node = NULL; } + if (NULL != (vpn_ctx = rh->vpn_ctx)) + { + GNUNET_VPN_cancel_request (vpn_ctx->vpn_request); + GNUNET_free (vpn_ctx->rd_data); + GNUNET_free (vpn_ctx); + } if (NULL != rh->dns_request) { GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); rh->dns_request = NULL; } - if (NULL != rh->namestore_qe) + if (NULL != rh->namecache_qe) + { + GNUNET_NAMECACHE_cancel (rh->namecache_qe); + rh->namecache_qe = NULL; + } + if (NULL != rh->rev_check) { - GNUNET_NAMESTORE_cancel (rh->namestore_qe); - rh->namestore_qe = NULL; + GNUNET_REVOCATION_query_cancel (rh->rev_check); + rh->rev_check = NULL; } if (NULL != rh->std_resolve) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Canceling standard DNS resolution\n"); GNUNET_RESOLVER_request_cancel (rh->std_resolve); rh->std_resolve = NULL; } @@ -2228,13 +2205,13 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) /** * Initialize the resolver * - * @param nh the namestore handle + * @param nc the namecache handle * @param dht the dht handle * @param c configuration handle * @param max_bg_queries maximum number of parallel background queries in dht */ void -GNS_resolver_init (struct GNUNET_NAMESTORE_Handle *nh, +GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc, struct GNUNET_DHT_Handle *dht, const struct GNUNET_CONFIGURATION_Handle *c, unsigned long long max_bg_queries) @@ -2242,7 +2219,7 @@ GNS_resolver_init (struct GNUNET_NAMESTORE_Handle *nh, char *dns_ip; cfg = c; - namestore_handle = nh; + namecache_handle = nc; dht_handle = dht; dht_lookup_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); @@ -2258,6 +2235,7 @@ GNS_resolver_init (struct GNUNET_NAMESTORE_Handle *nh, } dns_handle = GNUNET_DNSSTUB_start (dns_ip); GNUNET_free (dns_ip); + vpn_handle = GNUNET_VPN_connect (cfg); } @@ -2268,27 +2246,37 @@ void GNS_resolver_done () { struct GNS_ResolverHandle *rh; + struct CacheOps *co; /* abort active resolutions */ while (NULL != (rh = rlh_head)) { rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + GNS_resolver_lookup_cancel (rh); + } + while (NULL != (co = co_head)) + { + GNUNET_CONTAINER_DLL_remove (co_head, + co_tail, + co); + GNUNET_NAMECACHE_cancel (co->namecache_qe_cache); + GNUNET_free (co); } - /* abort active shorten operations */ - while (NULL != gph_head) - free_get_pseu_authority_handle (gph_head); GNUNET_CONTAINER_heap_destroy (dht_lookup_heap); dht_lookup_heap = NULL; GNUNET_DNSSTUB_stop (dns_handle); dns_handle = NULL; + GNUNET_VPN_disconnect (vpn_handle); + vpn_handle = NULL; + dht_handle = NULL; + namecache_handle = NULL; } /* *************** common helper functions (do not really belong here) *********** */ /** - * Checks if "name" ends in ".tld" + * Checks if @a name ends in ".TLD" * * @param name the name to check * @param tld the TLD to check for