2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
24 * @file gns/gns_resolver.c
25 * @brief GNUnet GNS service
26 * @author Martin Schanzenbach
29 #include "gnunet_util_lib.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_dns_service.h"
32 #include "gnunet_dht_service.h"
33 #include "gnunet_namestore_service.h"
34 #include "gnunet_dns_service.h"
35 #include "gnunet_dnsparser_lib.h"
36 #include "gnunet_gns_service.h"
37 #include "block_gns.h"
39 #include "gnunet-service-gns_resolver.h"
41 #define DHT_OPERATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
42 #define DHT_LOOKUP_TIMEOUT DHT_OPERATION_TIMEOUT
43 #define DHT_GNS_REPLICATION_LEVEL 5
44 #define MAX_DNS_LABEL_LENGTH 63
48 * Our handle to the namestore service
49 * FIXME maybe need a second handle for iteration
51 struct GNUNET_NAMESTORE_Handle *namestore_handle;
53 struct GNUNET_DHT_Handle *dht_handle;
62 * Helper function to free resolver handle
64 * @rh the handle to free
67 free_resolver_handle(struct ResolverHandle* rh)
69 struct AuthorityChain *ac;
70 struct AuthorityChain *ac_next;
75 GNUNET_free_non_null (rh->name);
76 GNUNET_free_non_null (rh->authority_name);
78 ac = rh->authority_chain_head;
83 GNUNET_free_non_null (ac->name);
92 * Callback when record data is put into namestore
94 * @param cls the closure
95 * @param success GNUNET_OK on success
96 * @param emsg the error message. NULL if SUCCESS==GNUNET_OK
99 on_namestore_record_put_result(void *cls,
103 if (GNUNET_NO == success)
105 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "records already in namestore\n");
108 else if (GNUNET_YES == success)
110 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
111 "records successfully put in namestore\n");
115 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
116 "Error putting records into namestore: %s\n", emsg);
121 * Handle timeout for DHT requests
123 * @param cls the request handle as closure
124 * @param tc the task context
127 dht_lookup_timeout(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
129 struct ResolverHandle *rh = cls;
131 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
132 "dht lookup for query %s timed out.\n",
135 GNUNET_DHT_get_stop (rh->get_handle);
136 rh->proc(rh->proc_cls, rh, 0, NULL);
141 * Function called when we get a result from the dht
142 * for our record query
144 * @param cls the request handle
145 * @param exp lifetime
146 * @param key the key the record was stored under
147 * @param get_path get path
148 * @param get_path_length get path length
149 * @param put_path put path
150 * @param put_path_length put path length
151 * @param type the block type
152 * @param size the size of the record
153 * @param data the record data
156 process_record_result_dht(void* cls,
157 struct GNUNET_TIME_Absolute exp,
158 const GNUNET_HashCode * key,
159 const struct GNUNET_PeerIdentity *get_path,
160 unsigned int get_path_length,
161 const struct GNUNET_PeerIdentity *put_path,
162 unsigned int put_path_length,
163 enum GNUNET_BLOCK_Type type,
164 size_t size, const void *data)
166 struct ResolverHandle *rh;
167 struct RecordLookupHandle *rlh;
168 struct GNSNameRecordBlock *nrb;
169 uint32_t num_records;
171 char* rd_data = (char*)data;
175 GNUNET_HashCode zone, name_hash;
176 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "got dht result (size=%d)\n", size);
181 //FIXME maybe check expiration here, check block type
183 rh = (struct ResolverHandle *)cls;
184 rlh = (struct RecordLookupHandle *) rh->proc_cls;
185 nrb = (struct GNSNameRecordBlock*)data;
187 /* stop lookup and timeout task */
188 GNUNET_DHT_get_stop (rh->get_handle);
189 GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
191 rh->get_handle = NULL;
192 name = (char*)&nrb[1];
193 num_records = ntohl(nrb->rd_count);
195 struct GNUNET_NAMESTORE_RecordData rd[num_records];
197 rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
198 rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
200 if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
205 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
209 for (i=0; i<num_records; i++)
211 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
212 "Got name: %s (wanted %s)\n", name, rh->name);
213 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
216 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
217 "Got data length: %d\n", rd[i].data_size);
218 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
219 "Got flag %d\n", rd[i].flags);
221 if ((strcmp(name, rh->name) == 0) &&
222 (rd[i].record_type == rlh->record_type))
229 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
230 GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
233 * FIXME check pubkey against existing key in namestore?
234 * https://gnunet.org/bugs/view.php?id=2179
237 /* Save to namestore */
238 GNUNET_NAMESTORE_record_put (namestore_handle,
245 &on_namestore_record_put_result, //cont
249 rh->proc(rh->proc_cls, rh, num_records, rd);
251 rh->proc(rh->proc_cls, rh, 0, NULL);
258 * Start DHT lookup for a (name -> query->record_type) record in
259 * rh->authority's zone
261 * @param rh the pending gns query context
264 resolve_record_dht(struct ResolverHandle *rh)
267 GNUNET_HashCode name_hash;
268 GNUNET_HashCode lookup_key;
269 struct GNUNET_CRYPTO_HashAsciiEncoded lookup_key_string;
270 struct RecordLookupHandle *rlh = (struct RecordLookupHandle *)rh->proc_cls;
272 GNUNET_CRYPTO_hash(rh->name, strlen(rh->name), &name_hash);
273 GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
274 GNUNET_CRYPTO_hash_to_enc (&lookup_key, &lookup_key_string);
276 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
277 "starting dht lookup for %s with key: %s\n",
278 rh->name, (char*)&lookup_key_string);
280 rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed(DHT_LOOKUP_TIMEOUT,
281 &dht_lookup_timeout, rh);
283 xquery = htonl(rlh->record_type);
284 rh->get_handle = GNUNET_DHT_get_start(dht_handle,
285 DHT_OPERATION_TIMEOUT,
286 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
288 DHT_GNS_REPLICATION_LEVEL,
292 &process_record_result_dht,
299 * Namestore calls this function if we have record for this name.
300 * (or with rd_count=0 to indicate no matches)
302 * @param cls the pending query
303 * @param key the key of the zone we did the lookup
304 * @param expiration expiration date of the namestore entry
305 * @param name the name for which we need an authority
306 * @param rd_count the number of records with 'name'
307 * @param rd the record data
308 * @param signature the signature of the authority for the record data
311 process_record_result_ns(void* cls,
312 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
313 struct GNUNET_TIME_Absolute expiration,
314 const char *name, unsigned int rd_count,
315 const struct GNUNET_NAMESTORE_RecordData *rd,
316 const struct GNUNET_CRYPTO_RsaSignature *signature)
318 struct ResolverHandle *rh;
319 struct RecordLookupHandle *rlh;
320 struct GNUNET_TIME_Relative remaining_time;
321 GNUNET_HashCode zone;
323 rh = (struct ResolverHandle *) cls;
324 rlh = (struct RecordLookupHandle *)rh->proc_cls;
325 GNUNET_CRYPTO_hash(key,
326 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
328 remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
334 rh->status |= EXISTS;
337 if (remaining_time.rel_value == 0)
339 rh->status |= EXPIRED;
345 * Lookup terminated and no results
347 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
348 "Namestore lookup for %s terminated without results\n", name);
350 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
351 "Record %s unknown in namestore\n",
354 * Our zone and no result? Cannot resolve TT
356 rh->proc(rh->proc_cls, rh, 0, NULL);
363 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
364 "Processing additional result %s from namestore\n", name);
366 for (i=0; i<rd_count;i++)
369 if (rd[i].record_type != rlh->record_type)
372 if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
375 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
376 "This record is expired. Skipping\n");
387 if (rh->answered == 0)
389 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
390 "No answers found. This is odd!\n");
391 rh->proc(rh->proc_cls, rh, 0, NULL);
395 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Found %d answer(s) to query!\n",
398 rh->proc(rh->proc_cls, rh, rd_count, rd);
404 * The final phase of resolution.
405 * rh->name is a name that is canonical and we do not have a delegation.
406 * Query namestore for this record
408 * @param rh the pending lookup
411 resolve_record_ns(struct ResolverHandle *rh)
413 struct RecordLookupHandle *rlh = (struct RecordLookupHandle *)rh->proc_cls;
416 * Try to resolve this record in our namestore.
417 * The name to resolve is now in rh->authority_name
418 * since we tried to resolve it to an authority
421 GNUNET_NAMESTORE_lookup_record(namestore_handle,
425 &process_record_result_ns,
431 * Handle timeout for DHT requests
433 * @param cls the request handle as closure
434 * @param tc the task context
437 dht_authority_lookup_timeout(void *cls,
438 const struct GNUNET_SCHEDULER_TaskContext *tc)
440 struct ResolverHandle *rh = cls;
442 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
443 "dht lookup for query %s timed out.\n",
446 GNUNET_DHT_get_stop (rh->get_handle);
447 if (strcmp(rh->name, "") == 0)
450 * promote authority back to name and try to resolve record
452 strcpy(rh->name, rh->authority_name);
454 rh->proc(rh->proc_cls, rh, 0, NULL);
458 static void resolve_delegation_dht(struct ResolverHandle *rh);
461 * Function called when we get a result from the dht
462 * for our query. Recursively tries to resolve authorities
465 * @param cls the request handle
466 * @param exp lifetime
467 * @param key the key the record was stored under
468 * @param get_path get path
469 * @param get_path_length get path length
470 * @param put_path put path
471 * @param put_path_length put path length
472 * @param type the block type
473 * @param size the size of the record
474 * @param data the record data
477 process_delegation_result_dht(void* cls,
478 struct GNUNET_TIME_Absolute exp,
479 const GNUNET_HashCode * key,
480 const struct GNUNET_PeerIdentity *get_path,
481 unsigned int get_path_length,
482 const struct GNUNET_PeerIdentity *put_path,
483 unsigned int put_path_length,
484 enum GNUNET_BLOCK_Type type,
485 size_t size, const void *data)
487 struct ResolverHandle *rh;
488 struct GNSNameRecordBlock *nrb;
489 uint32_t num_records;
491 char* rd_data = (char*) data;
494 GNUNET_HashCode zone, name_hash;
496 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got DHT result\n");
501 //FIXME check expiration?
503 rh = (struct ResolverHandle *)cls;
504 nrb = (struct GNSNameRecordBlock*)data;
506 /* stop dht lookup and timeout task */
507 GNUNET_DHT_get_stop (rh->get_handle);
508 GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
510 rh->get_handle = NULL;
511 num_records = ntohl(nrb->rd_count);
512 name = (char*)&nrb[1];
514 struct GNUNET_NAMESTORE_RecordData rd[num_records];
516 rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
517 rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
519 if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
524 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
528 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
529 "Got name: %s (wanted %s)\n", name, rh->authority_name);
530 for (i=0; i<num_records; i++)
533 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
534 "Got name: %s (wanted %s)\n", name, rh->authority_name);
535 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
536 "Got type: %d (wanted %d)\n",
537 rd[i].record_type, GNUNET_GNS_RECORD_PKEY);
538 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
539 "Got data length: %d\n", rd[i].data_size);
540 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
541 "Got flag %d\n", rd[i].flags);
543 if ((strcmp(name, rh->authority_name) == 0) &&
544 (rd[i].record_type == GNUNET_GNS_RECORD_PKEY))
546 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Authority found in DHT\n");
548 memcpy(&rh->authority, rd[i].data, sizeof(GNUNET_HashCode));
549 struct AuthorityChain *auth =
550 GNUNET_malloc(sizeof(struct AuthorityChain));
551 auth->zone = rh->authority;
552 auth->name = GNUNET_malloc(strlen(rh->authority_name)+1);
553 memset(auth->name, 0, strlen(rh->authority_name)+1);
554 strcpy(auth->name, rh->authority_name);
555 GNUNET_CONTAINER_DLL_insert (rh->authority_chain_head,
556 rh->authority_chain_tail,
563 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
564 GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
566 /* Save to namestore */
567 if (0 != GNUNET_CRYPTO_hash_cmp(&rh->authority_chain_tail->zone, &zone))
569 GNUNET_NAMESTORE_record_put (namestore_handle,
576 &on_namestore_record_put_result, //cont
586 * FIXME in this case. should we ask namestore again?
588 if (strcmp(rh->name, "") == 0)
589 rh->proc(rh->proc_cls, rh, 0, NULL);
591 resolve_delegation_dht(rh);
596 * should never get here unless false dht key/put
597 * block plugin should handle this
599 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "DHT authority lookup error!\n");
605 * Process DHT lookup result for record.
607 * @param cls the closure
608 * @param rh resolver handle
609 * @param rd_count number of results (always 0)
610 * @param rd record data (always NULL)
613 handle_record_dht(void* cls, struct ResolverHandle *rh,
614 unsigned int rd_count,
615 const struct GNUNET_NAMESTORE_RecordData *rd)
617 struct RecordLookupHandle* rlh;
618 rlh = (struct RecordLookupHandle*)cls;
621 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
622 "No records for %s found in DHT. Aborting\n",
624 /* give up, cannot resolve */
625 rlh->proc(rlh->proc_cls, 0, NULL);
629 /* results found yay */
630 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
631 "Record resolved from namestore!");
632 rlh->proc(rlh->proc_cls, rd_count, rd);
638 * Process namestore lookup result for record.
640 * @param cls the closure
641 * @param rh resolver handle
642 * @param rd_count number of results (always 0)
643 * @param rd record data (always NULL)
646 handle_record_ns(void* cls, struct ResolverHandle *rh,
647 unsigned int rd_count,
648 const struct GNUNET_NAMESTORE_RecordData *rd)
650 struct RecordLookupHandle* rlh;
651 rlh = (struct RecordLookupHandle*) cls;
654 /* ns entry expired. try dht */
655 if (rh->status & (EXPIRED | !EXISTS))
657 rh->proc = &handle_record_dht;
658 resolve_record_dht(rh);
661 /* give up, cannot resolve */
662 rlh->proc(rlh->proc_cls, 0, NULL);
666 /* results found yay */
667 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
668 "Record resolved from namestore!");
669 rlh->proc(rlh->proc_cls, rd_count, rd);
675 * Determine if this name is canonical.
677 * a.b.gnunet = not canonical
680 * @param name the name to test
681 * @return 1 if canonical
684 is_canonical(char* name)
686 uint32_t len = strlen(name);
689 for (i=0; i<len; i++)
691 if (*(name+i) == '.')
698 * Move one level up in the domain hierarchy and return the
699 * passed top level domain.
701 * @param name the domain
702 * @param dest the destination where the tld will be put
705 pop_tld(char* name, char* dest)
709 if (is_canonical(name))
716 for (len = strlen(name); len > 0; len--)
718 if (*(name+len) == '.')
728 strcpy(dest, (name+len+1));
732 * DHT resolution for delegation finished. Processing result.
734 * @param cls the closure
735 * @param rh resolver handle
736 * @param rd_count number of results (always 0)
737 * @param rd record data (always NULL)
740 handle_delegation_dht(void* cls, struct ResolverHandle *rh,
741 unsigned int rd_count,
742 const struct GNUNET_NAMESTORE_RecordData *rd)
744 struct RecordLookupHandle* rlh;
745 rlh = (struct RecordLookupHandle*) cls;
747 if (strcmp(rh->name, "") == 0)
749 /* We resolved full name for delegation. resolving record */
750 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
751 "Resolved full name for delegation via DHT. resolving record '' in ns\n");
752 rh->proc = &handle_record_ns;
753 resolve_record_ns(rh);
758 * we still have some left
760 if (is_canonical(rh->name))
762 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
763 "Resolving canonical record %s in ns\n", rh->name);
764 rh->proc = &handle_record_ns;
765 resolve_record_ns(rh);
768 /* give up, cannot resolve */
769 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
770 "Cannot fully resolve delegation for %s via DHT!\n",
772 rlh->proc(rlh->proc_cls, 0, NULL);
777 * Start DHT lookup for a name -> PKEY (compare NS) record in
778 * rh->authority's zone
780 * @param rh the pending gns query
781 * @param name the name of the PKEY record
784 resolve_delegation_dht(struct ResolverHandle *rh)
787 GNUNET_HashCode name_hash;
788 GNUNET_HashCode lookup_key;
790 GNUNET_CRYPTO_hash(rh->authority_name,
791 strlen(rh->authority_name),
793 GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
795 rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
796 &dht_authority_lookup_timeout,
799 xquery = htonl(GNUNET_GNS_RECORD_PKEY);
801 rh->get_handle = GNUNET_DHT_get_start(dht_handle,
802 DHT_OPERATION_TIMEOUT,
803 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
805 DHT_GNS_REPLICATION_LEVEL,
809 &process_delegation_result_dht,
816 * Namestore resolution for delegation finished. Processing result.
818 * @param cls the closure
819 * @param rh resolver handle
820 * @param rd_count number of results (always 0)
821 * @param rd record data (always NULL)
824 handle_delegation_ns(void* cls, struct ResolverHandle *rh,
825 unsigned int rd_count,
826 const struct GNUNET_NAMESTORE_RecordData *rd)
828 struct RecordLookupHandle* rlh;
829 rlh = (struct RecordLookupHandle*) cls;
831 if (strcmp(rh->name, "") == 0)
833 /* We resolved full name for delegation. resolving record */
834 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
835 "Resolved full name for delegation. resolving record ''\n");
836 rh->proc = &handle_record_ns;
837 resolve_record_ns(rh);
842 * we still have some left
843 * check if ns entry is fresh
845 if (rh->status & (EXISTS | !EXPIRED))
847 if (is_canonical(rh->name))
849 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
850 "Resolving canonical record %s\n", rh->name);
851 rh->proc = &handle_record_ns;
852 resolve_record_ns(rh);
856 /* give up, cannot resolve */
857 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
858 "Cannot fully resolve delegation for %s!\n",
860 rlh->proc(rlh->proc_cls, 0, NULL);
865 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
866 "Trying to resolve delegation for %s via DHT\n",
868 rh->proc = &handle_delegation_dht;
869 resolve_delegation_dht(rh);
873 static void resolve_delegation_ns(struct ResolverHandle *rh);
877 * This is a callback function that should give us only PKEY
878 * records. Used to query the namestore for the authority (PKEY)
879 * for 'name'. It will recursively try to resolve the
880 * authority for a given name from the namestore.
882 * @param cls the pending query
883 * @param key the key of the zone we did the lookup
884 * @param expiration expiration date of the record data set in the namestore
885 * @param name the name for which we need an authority
886 * @param rd_count the number of records with 'name'
887 * @param rd the record data
888 * @param signature the signature of the authority for the record data
891 process_delegation_result_ns(void* cls,
892 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
893 struct GNUNET_TIME_Absolute expiration,
895 unsigned int rd_count,
896 const struct GNUNET_NAMESTORE_RecordData *rd,
897 const struct GNUNET_CRYPTO_RsaSignature *signature)
899 struct ResolverHandle *rh;
900 struct GNUNET_TIME_Relative remaining_time;
901 GNUNET_HashCode zone;
904 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got %d records from authority lookup\n",
907 rh = (struct ResolverHandle *)cls;
908 GNUNET_CRYPTO_hash(key,
909 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
911 remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
917 rh->status |= EXISTS;
920 if (remaining_time.rel_value == 0)
922 rh->status |= EXPIRED;
926 * No authority found in namestore.
931 * We did not find an authority in the namestore
936 * Promote this authority back to a name maybe it is
939 if (strcmp(rh->name, "") == 0)
941 /* simply promote back */
942 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
943 "Promoting %s back to name\n", rh->authority_name);
944 strcpy(rh->name, rh->authority_name);
948 /* add back to existing name */
949 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
950 "Adding %s back to %s\n",
951 rh->authority_name, rh->name);
952 new_name = GNUNET_malloc(strlen(rh->name)
953 + strlen(rh->authority_name) + 2);
954 memset(new_name, 0, strlen(rh->name) + strlen(rh->authority_name) + 2);
955 strcpy(new_name, rh->name);
956 strcpy(new_name+strlen(new_name)+1, ".");
957 strcpy(new_name+strlen(new_name)+2, rh->authority_name);
958 GNUNET_free(rh->name);
961 rh->proc(rh->proc_cls, rh, 0, NULL);
965 //Note only 1 pkey should have been returned.. anything else would be strange
967 * We found an authority that may be able to help us
971 for (i=0; i<rd_count;i++)
974 if (rd[i].record_type != GNUNET_GNS_RECORD_PKEY)
977 if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
980 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "This pkey is expired.\n");
981 if (remaining_time.rel_value == 0)
983 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
984 "This dht entry is expired.\n");
985 rh->authority_chain_head->fresh = 0;
986 rh->proc(rh->proc_cls, rh, 0, NULL);
994 * Resolve rest of query with new authority
996 GNUNET_assert(rd[i].record_type == GNUNET_GNS_RECORD_PKEY);
997 memcpy(&rh->authority, rd[i].data, sizeof(GNUNET_HashCode));
998 struct AuthorityChain *auth = GNUNET_malloc(sizeof(struct AuthorityChain));
999 auth->zone = rh->authority;
1000 auth->name = GNUNET_malloc(strlen(rh->authority_name)+1);
1001 memset(auth->name, 0, strlen(rh->authority_name)+1);
1002 strcpy(auth->name, rh->authority_name);
1003 GNUNET_CONTAINER_DLL_insert (rh->authority_chain_head,
1004 rh->authority_chain_tail,
1008 * We are done with PKEY resolution if name is empty
1009 * else resolve again with new authority
1011 if (strcmp(rh->name, "") == 0)
1012 rh->proc(rh->proc_cls, rh, 0, NULL);
1014 resolve_delegation_ns(rh);
1021 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1022 "Authority lookup successful but no PKEY... never get here\n");
1023 rh->proc(rh->proc_cls, rh, 0, NULL);
1028 * Resolve the delegation chain for the request in our namestore
1030 * @param rh the resolver handle
1033 resolve_delegation_ns(struct ResolverHandle *rh)
1035 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1036 "Resolving delegation for %s\n", rh->name);
1037 pop_tld(rh->name, rh->authority_name);
1038 GNUNET_NAMESTORE_lookup_record(namestore_handle,
1041 GNUNET_GNS_RECORD_PKEY,
1042 &process_delegation_result_ns,
1049 * Lookup of a record in a specific zone
1050 * calls lookup result processor on result
1052 * @param zone the root zone
1053 * @param record_type the record type to look up
1054 * @param proc the processor to call
1055 * @param cls the closure to pass to proc
1058 gns_resolver_lookup_record(GNUNET_HashCode zone,
1059 uint32_t record_type,
1061 RecordLookupProcessor proc,
1064 struct ResolverHandle *rh;
1065 struct RecordLookupHandle* rlh;
1067 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1068 "Starting resolution for %s (type=%d)!\n",
1071 rlh = GNUNET_malloc(sizeof(struct RecordLookupHandle));
1072 rh = GNUNET_malloc(sizeof (struct ResolverHandle));
1074 rh->authority = zone;
1076 rh->name = GNUNET_malloc(strlen(name)
1077 - strlen(GNUNET_GNS_TLD));
1079 strlen(name)-strlen(GNUNET_GNS_TLD));
1080 memcpy(rh->name, name,
1081 strlen(name)-strlen(GNUNET_GNS_TLD) - 1);
1082 rh->authority_name = GNUNET_malloc(sizeof(char)*MAX_DNS_LABEL_LENGTH);
1083 rh->authority_chain_head = GNUNET_malloc(sizeof(struct AuthorityChain));
1084 rh->authority_chain_head->prev = NULL;
1085 rh->authority_chain_head->next = NULL;
1086 rh->authority_chain_tail = rh->authority_chain_head;
1087 rh->authority_chain_head->zone = zone;
1089 rlh->record_type = record_type;
1090 rlh->name = (char*)name; //FIXME
1092 rlh->proc_cls = cls;
1094 rh->proc = &handle_delegation_ns;
1095 resolve_delegation_ns(rh);
1098 /******** END Record Resolver ***********/
1102 process_pseu_result_ns_shorten(void *cls,
1103 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
1104 struct GNUNET_TIME_Absolute expire,
1106 unsigned int rd_len,
1107 const struct GNUNET_NAMESTORE_RecordData *rd,
1108 const struct GNUNET_CRYPTO_RsaSignature *signature)
1110 struct ResolverHandle *rh =
1111 (struct ResolverHandle *)cls;
1112 struct GNUNET_TIME_Relative remaining_time;
1114 GNUNET_TIME_absolute_get_remaining (expire);
1119 rh->status |= EXISTS;
1122 if (remaining_time.rel_value == 0)
1124 rh->status |= EXPIRED;
1127 rh->proc(rh->proc_cls, rh, rd_len, rd);
1132 * Function called when we get a result from the dht
1133 * for our record query
1135 * @param cls the request handle
1136 * @param exp lifetime
1137 * @param key the key the record was stored under
1138 * @param get_path get path
1139 * @param get_path_length get path length
1140 * @param put_path put path
1141 * @param put_path_length put path length
1142 * @param type the block type
1143 * @param size the size of the record
1144 * @param data the record data
1147 process_pseu_dht_result(void* cls,
1148 struct GNUNET_TIME_Absolute exp,
1149 const GNUNET_HashCode * key,
1150 const struct GNUNET_PeerIdentity *get_path,
1151 unsigned int get_path_length,
1152 const struct GNUNET_PeerIdentity *put_path,
1153 unsigned int put_path_length,
1154 enum GNUNET_BLOCK_Type type,
1155 size_t size, const void *data)
1157 struct ResolverHandle *rh;
1158 struct RecordLookupHandle *rlh;
1159 struct GNSNameRecordBlock *nrb;
1160 uint32_t num_records;
1162 char* rd_data = (char*)data;
1166 GNUNET_HashCode zone, name_hash;
1167 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "got PSEU dht result (size=%d)\n", size);
1172 //FIXME maybe check expiration here, check block type
1174 rh = (struct ResolverHandle *)cls;
1175 rlh = (struct RecordLookupHandle *) rh->proc_cls;
1176 nrb = (struct GNSNameRecordBlock*)data;
1178 /* stop lookup and timeout task */
1179 GNUNET_DHT_get_stop (rh->get_handle);
1180 GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
1182 rh->get_handle = NULL;
1183 name = (char*)&nrb[1];
1184 num_records = ntohl(nrb->rd_count);
1186 struct GNUNET_NAMESTORE_RecordData rd[num_records];
1188 rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
1189 rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
1191 if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
1196 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
1200 for (i=0; i<num_records; i++)
1202 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1203 "Got name: %s (wanted %s)\n", name, rh->name);
1204 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1207 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1208 "Got data length: %d\n", rd[i].data_size);
1209 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1210 "Got flag %d\n", rd[i].flags);
1212 if ((strcmp(name, "+") == 0) &&
1213 (rd[i].record_type == GNUNET_GNS_RECORD_PSEU))
1220 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
1221 GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
1224 * FIXME check pubkey against existing key in namestore?
1225 * https://gnunet.org/bugs/view.php?id=2179
1228 /* Save to namestore */
1229 GNUNET_NAMESTORE_record_put (namestore_handle,
1236 &on_namestore_record_put_result, //cont
1240 rh->proc(rh->proc_cls, rh, num_records, rd);
1242 rh->proc(rh->proc_cls, rh, 0, NULL);
1249 * Start DHT lookup for a PSEUdonym record in
1250 * rh->authority's zone
1252 * @param rh the pending gns query
1255 resolve_pseu_dht(struct ResolverHandle *rh)
1258 GNUNET_HashCode name_hash;
1259 GNUNET_HashCode lookup_key;
1261 GNUNET_CRYPTO_hash("+",
1265 GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
1267 rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
1268 &dht_lookup_timeout,
1271 xquery = htonl(GNUNET_GNS_RECORD_PSEU);
1273 rh->get_handle = GNUNET_DHT_get_start(dht_handle,
1274 DHT_OPERATION_TIMEOUT,
1275 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
1277 DHT_GNS_REPLICATION_LEVEL,
1281 &process_pseu_dht_result,
1288 handle_shorten_pseu_ns_result(void* cls,
1289 struct ResolverHandle *rh,
1291 const struct GNUNET_NAMESTORE_RecordData *rd);
1294 process_zone_to_name_shorten(void *cls,
1295 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
1296 struct GNUNET_TIME_Absolute expire,
1298 unsigned int rd_len,
1299 const struct GNUNET_NAMESTORE_RecordData *rd,
1300 const struct GNUNET_CRYPTO_RsaSignature *signature)
1302 struct ResolverHandle *rh =
1303 (struct ResolverHandle *)cls;
1304 struct NameShortenHandle* nsh = (struct NameShortenHandle*)rh->proc_cls;
1305 struct AuthorityChain *next_authority;
1308 char* next_authority_name;
1311 /* we found a match in our own zone */
1314 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1315 "result strlen %d\n", strlen(name));
1316 answer_len = strlen(rh->name) + strlen(name) + strlen(GNUNET_GNS_TLD) + 3;
1317 result = GNUNET_malloc(answer_len);
1318 memset(result, 0, answer_len);
1319 strcpy(result, rh->name);
1320 strcpy(result+strlen(rh->name), ".");
1321 strcpy(result+strlen(rh->name)+1, name);
1322 strcpy(result+strlen(rh->name)+strlen(name)+1, ".");
1323 strcpy(result+strlen(rh->name)+strlen(name)+2, GNUNET_GNS_TLD);
1325 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1326 "Sending shorten result %s\n", result);
1328 nsh->proc(nsh->proc_cls, result);
1329 free_resolver_handle(rh);
1330 GNUNET_free(result);
1332 else if (GNUNET_CRYPTO_hash_cmp(&rh->authority_chain_head->zone,
1333 &rh->authority_chain_tail->zone))
1335 /* our zone, just append .gnunet */
1336 answer_len = strlen(rh->name) + strlen(GNUNET_GNS_TLD) + 2;
1337 result = GNUNET_malloc(answer_len);
1338 memset(result, 0, answer_len);
1339 strcpy(result, rh->name);
1340 strcpy(result+strlen(rh->name), ".");
1341 strcpy(result+strlen(rh->name)+1, GNUNET_GNS_TLD);
1343 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1344 "Our zone: Sending name as shorten result %s\n", rh->name);
1346 nsh->proc(nsh->proc_cls, result); //FIXME +.gnunet!
1347 free_resolver_handle(rh);
1348 GNUNET_free(result);
1354 * continue with next authority
1356 next_authority = rh->authority_chain_head;
1357 next_authority_name = GNUNET_malloc(strlen(rh->name)+
1358 strlen(next_authority->name) + 2);
1359 memset(next_authority_name, 0, strlen(rh->name)+
1360 strlen(next_authority->name) + 2);
1361 strcpy(next_authority_name, rh->name);
1362 strcpy(next_authority_name+strlen(rh->name)+1, ".");
1363 strcpy(next_authority_name+strlen(rh->name)+2, next_authority->name);
1365 GNUNET_free(rh->name);
1366 rh->name = next_authority_name;
1367 GNUNET_CONTAINER_DLL_remove(rh->authority_chain_head,
1368 rh->authority_chain_tail,
1371 GNUNET_NAMESTORE_zone_to_name (namestore_handle,
1372 &rh->authority_chain_tail->zone,
1373 &rh->authority_chain_head->zone,
1374 &process_zone_to_name_shorten,
1381 * Process result from namestore PSEU lookup
1382 * for shorten operation
1384 * @param cls the client shorten handle
1385 * @param rh the resolver handle
1386 * @param rd_count number of results (0 if none found)
1387 * @param rd data (NULL if none found)
1390 handle_pseu_ns_result_shorten(void* cls,
1391 struct ResolverHandle *rh,
1393 const struct GNUNET_NAMESTORE_RecordData *rd)
1395 struct NameShortenHandle* nsh = (struct NameShortenHandle*) cls;
1396 struct AuthorityChain *next_authority;
1408 for (i=0; i < rd_len; i++)
1410 if (rd[i].record_type == GNUNET_GNS_RECORD_PSEU)
1412 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1413 "Found PSEU %s len %d\n", (char*) rd[i].data,
1414 strlen((char*) rd[i].data));
1419 pseu = (char*) rd[i].data;
1420 answer_len = strlen(rh->name) + strlen(pseu) + strlen(GNUNET_GNS_TLD) + 3;
1421 result = GNUNET_malloc(answer_len);
1422 memset(result, 0, answer_len);
1423 strcpy(result, rh->name);
1424 strcpy(result+strlen(rh->name), ".");
1425 strcpy(result+strlen(rh->name)+1, pseu);
1426 strcpy(result+strlen(rh->name)+strlen(pseu)+1, ".");
1427 strcpy(result+strlen(rh->name)+strlen(pseu)+2, GNUNET_GNS_TLD);
1429 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1430 "Sending shorten result %s\n", result);
1432 nsh->proc(nsh->proc_cls, result);
1433 free_resolver_handle(rh);
1434 GNUNET_free(result);
1440 * continue with next authority
1441 * Note that we never have <2 authorities
1442 * in our list at this point since tail is always our root
1443 * And we filter fot this in handle_delegation_ns_shorten
1445 next_authority = rh->authority_chain_head;
1446 new_name = GNUNET_malloc(strlen(rh->name)+
1447 strlen(next_authority->name) + 2);
1448 memset(new_name, 0, strlen(rh->name)+
1449 strlen(next_authority->name) + 2);
1450 strcpy(new_name, rh->name);
1451 strcpy(new_name+strlen(rh->name)+1, ".");
1452 strcpy(new_name+strlen(rh->name)+2, next_authority->name);
1454 GNUNET_free(rh->name);
1455 rh->name = new_name;
1456 GNUNET_CONTAINER_DLL_remove(rh->authority_chain_head,
1457 rh->authority_chain_tail,
1460 GNUNET_NAMESTORE_zone_to_name (namestore_handle,
1461 &rh->authority_chain_tail->zone,
1462 &rh->authority_chain_head->zone,
1463 &process_zone_to_name_shorten,
1471 * Process result from namestore delegation lookup
1472 * for shorten operation
1474 * @param cls the client shorten handle
1475 * @param rh the resolver handle
1476 * @param rd_count number of results (0)
1477 * @param rd data (NULL)
1480 handle_delegation_ns_shorten(void* cls,
1481 struct ResolverHandle *rh,
1483 const struct GNUNET_NAMESTORE_RecordData *rd)
1485 struct NameShortenHandle *nsh;
1489 nsh = (struct NameShortenHandle *)cls;
1492 * At this point rh->name contains the part of the name
1493 * that we do not have a PKEY in our namestore to resolve.
1494 * The authority chain in the resolver handle is now
1495 * useful to backtrack if needed
1498 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1499 "PKEY resolved as far as possible in ns up to %s!\n", rh->name);
1501 if (GNUNET_CRYPTO_hash_cmp(&rh->authority_chain_head->zone,
1502 &rh->authority_chain_tail->zone) == 0)
1505 * This is our zone append .gnunet unless name is empty
1506 * (it shouldn't be, usually FIXME what happens if we
1507 * shorten to our zone to a "" record??)
1510 answer_len = strlen(rh->name) + strlen(GNUNET_GNS_TLD) + 2;
1511 result = GNUNET_malloc(answer_len);
1512 memset(result, 0, answer_len);
1513 strcpy(result, rh->name);
1514 strcpy(result+strlen(rh->name), ".");
1515 strcpy(result+strlen(rh->name)+1, GNUNET_GNS_TLD);
1517 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1518 "Our zone: Sending name as shorten result %s\n", rh->name);
1520 nsh->proc(nsh->proc_cls, result); //FIXME +.gnunet!
1521 free_resolver_handle(rh);
1522 GNUNET_free(result);
1526 /* backtrack authorities for names */
1527 GNUNET_NAMESTORE_zone_to_name (namestore_handle,
1528 &rh->authority_chain_tail->zone, //ours
1529 &rh->authority_chain_head->zone,
1530 &process_zone_to_name_shorten,
1536 * Shorten api from resolver
1538 * @param zone the zone to use
1539 * @param name the name to shorten
1540 * @param proc the processor to call with result
1541 * @param cls closure to pass to proc
1544 gns_resolver_shorten_name(GNUNET_HashCode zone,
1546 ShortenResultProcessor proc,
1549 struct ResolverHandle *rh;
1550 struct NameShortenHandle *nsh;
1552 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1553 "Starting shorten for %s!\n", name);
1555 nsh = GNUNET_malloc(sizeof (struct NameShortenHandle));
1556 rh = GNUNET_malloc(sizeof (struct ResolverHandle));
1557 rh->authority = zone;
1558 rh->name = GNUNET_malloc(strlen(name)
1559 - strlen(GNUNET_GNS_TLD));
1561 strlen(name)-strlen(GNUNET_GNS_TLD));
1562 memcpy(rh->name, name,
1563 strlen(name)-strlen(GNUNET_GNS_TLD)-1);
1565 rh->authority_name = GNUNET_malloc(sizeof(char)*MAX_DNS_LABEL_LENGTH);
1566 rh->authority_chain_head = GNUNET_malloc(sizeof(struct AuthorityChain));
1567 rh->authority_chain_tail = rh->authority_chain_head;
1568 rh->authority_chain_head->zone = zone;
1569 rh->proc = &handle_delegation_ns_shorten;
1573 nsh->proc_cls = cls;
1575 /* Start delegation resolution in our namestore */
1576 resolve_delegation_ns(rh);
1579 /*********** END NAME SHORTEN ********************/
1583 * Process result from namestore delegation lookup
1584 * for get authority operation
1586 * @param cls the client get auth handle
1587 * @param rh the resolver handle
1588 * @param rd_count number of results (0)
1589 * @param rd data (NULL)
1592 handle_delegation_result_ns_get_auth(void* cls,
1593 struct ResolverHandle *rh,
1595 const struct GNUNET_NAMESTORE_RecordData *rd)
1597 struct GetNameAuthorityHandle* nah;
1601 nah = (struct GetNameAuthorityHandle*) rh->proc_cls;
1604 * At this point rh->name contains the part of the name
1605 * that we do not have a PKEY in our namestore to resolve.
1606 * The authority chain in the resolver handle is now
1607 * useful to backtrack if needed
1610 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1611 "PKEY resolved as far as possible in ns up to %s!\n", rh->name);
1613 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1614 "Building response!\n");
1615 if (is_canonical(rh->name))
1618 * We successfully resolved the authority in the ns
1619 * FIXME for our purposes this is fine
1620 * but maybe we want to have an api that also looks
1621 * into the dht (i.e. option in message)
1623 if (strlen(rh->name) > strlen(nah->name))
1625 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1626 "Record name longer than original lookup name... odd!\n");
1630 answer_len = strlen(nah->name) - strlen(rh->name)
1631 + strlen(GNUNET_GNS_TLD) + 1;
1632 result = GNUNET_malloc(answer_len);
1633 memset(result, 0, answer_len);
1634 strcpy(result, nah->name + strlen(rh->name) + 1);
1636 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1637 "Got authority result %s\n", result);
1639 nah->proc(nah->proc_cls, result);
1640 free_resolver_handle(rh);
1641 GNUNET_free(result);
1646 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1647 "Unable to resolve authority for remaining %s!\n", rh->name);
1648 nah->proc(nah->proc_cls, "");
1649 free_resolver_handle(rh);
1658 * Tries to resolve the authority for name
1661 * @param zone the root zone to look up for
1662 * @param name the name to lookup up
1663 * @param proc the processor to call when finished
1664 * @param cls the closure to pass to the processor
1667 gns_resolver_get_authority(GNUNET_HashCode zone,
1669 GetAuthorityResultProcessor proc,
1672 struct ResolverHandle *rh;
1673 struct GetNameAuthorityHandle *nah;
1675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1676 "Starting authority resolution for %s!\n", name);
1678 nah = GNUNET_malloc(sizeof (struct GetNameAuthorityHandle));
1679 rh = GNUNET_malloc(sizeof (struct ResolverHandle));
1680 rh->authority = zone;
1682 rh->name = GNUNET_malloc(strlen(name)
1683 - strlen(GNUNET_GNS_TLD));
1685 strlen(name)-strlen(GNUNET_GNS_TLD));
1686 memcpy(rh->name, name,
1687 strlen(name)-strlen(GNUNET_GNS_TLD) - 1);
1689 nah->name = GNUNET_malloc(strlen(name)+1);
1690 memset(nah->name, 0,
1692 strcpy(nah->name, name);
1694 rh->authority_name = GNUNET_malloc(MAX_DNS_LABEL_LENGTH);
1696 rh->authority_chain_head = GNUNET_malloc(sizeof(struct AuthorityChain));
1697 rh->authority_chain_tail = rh->authority_chain_head;
1698 rh->authority_chain_head->zone = zone;
1699 rh->proc = &handle_delegation_result_ns_get_auth;
1700 rh->proc_cls = (void*)nah;
1703 nah->proc_cls = cls;
1705 /* Start delegation resolution in our namestore */
1706 resolve_delegation_ns(rh);
1710 /******** END GET AUTHORITY *************/
1712 /* end of gns_resolver.c */