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 * - Write xquery and block plugin
25 * - The smaller FIXME issues all around
27 * @file gns/gnunet-service-gns.c
28 * @brief GNUnet GNS service
29 * @author Martin Schanzenbach
32 #include "gnunet_util_lib.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_dns_service.h"
35 #include "gnunet_dnsparser_lib.h"
36 #include "gnunet_dht_service.h"
37 #include "gnunet_namestore_service.h"
38 #include "gnunet_gns_service.h"
39 #include "block_gns.h"
42 #define DHT_OPERATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
43 #define DHT_LOOKUP_TIMEOUT DHT_OPERATION_TIMEOUT
44 #define DHT_GNS_REPLICATION_LEVEL 5
45 #define MAX_DNS_LABEL_LENGTH 63
47 /* Ignore for now not used anyway and probably never will */
48 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP 23
49 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT 24
50 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN 25
51 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT 26
54 * Handle to a currenty pending resolution
56 struct GNUNET_GNS_ResolverHandle
58 /* The name to resolve */
61 /* the request handle to reply to */
62 struct GNUNET_DNS_RequestHandle *request_handle;
64 /* the dns parser packet received */
65 struct GNUNET_DNSPARSER_Packet *packet;
67 /* the query parsed from the packet */
69 struct GNUNET_DNSPARSER_Query *query;
71 /* has this query been answered? how many matches */
74 /* the authoritative zone to query */
75 GNUNET_HashCode authority;
77 /* the name of the authoritative zone to query */
81 * we have an authority in namestore that
82 * may be able to resolve
86 /* a handle for dht lookups. should be NULL if no lookups are in progress */
87 struct GNUNET_DHT_GetHandle *get_handle;
89 /* timeout task for dht lookups */
90 GNUNET_SCHEDULER_TaskIdentifier dht_timeout_task;
94 struct ClientShortenHandle
96 struct GNUNET_SERVER_Client *client;
102 * Our handle to the DNS handler library
104 struct GNUNET_DNS_Handle *dns_handle;
107 * Our handle to the DHT
109 struct GNUNET_DHT_Handle *dht_handle;
112 * Our zone's private key
114 struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
117 * Our handle to the namestore service
118 * FIXME maybe need a second handle for iteration
120 struct GNUNET_NAMESTORE_Handle *namestore_handle;
123 * Handle to iterate over our authoritative zone in namestore
125 struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
128 * The configuration the GNS service is running with
130 const struct GNUNET_CONFIGURATION_Handle *GNS_cfg;
133 * Our notification context.
135 static struct GNUNET_SERVER_NotificationContext *nc;
140 GNUNET_HashCode zone_hash;
143 * Our tld. Maybe get from config file
145 const char* gnunet_tld = ".gnunet";
148 * Useful for zone update for DHT put
150 static int num_public_records = 3600;
151 struct GNUNET_TIME_Relative dht_update_interval;
152 GNUNET_SCHEDULER_TaskIdentifier zone_update_taskid = GNUNET_SCHEDULER_NO_TASK;
154 static void resolve_name(struct GNUNET_GNS_ResolverHandle *rh);
157 * Reply to client with the result from our lookup.
159 * @param rh the request handle of the lookup
160 * @param rd_count the number of records to return
161 * @param rd the record data
164 reply_to_dns(struct GNUNET_GNS_ResolverHandle *rh, uint32_t rd_count,
165 const struct GNUNET_NAMESTORE_RecordData *rd)
171 struct GNUNET_DNSPARSER_Packet *packet = rh->packet;
172 struct GNUNET_DNSPARSER_Record answer_records[rh->answered];
173 struct GNUNET_DNSPARSER_Record additional_records[rd_count-(rh->answered)];
174 packet->answers = answer_records;
175 packet->additional_records = additional_records;
178 * Put records in the DNS packet and modify it
181 len = sizeof(struct GNUNET_DNSPARSER_Record*);
182 for (i=0; i < rd_count; i++)
185 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
186 "Adding type %d to DNS response\n", rd[i].record_type);
187 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Name: %s\n", rh->name);
188 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "QName: %s\n", rh->query->name);
189 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record %d/%d\n", i+1, rd_count);
190 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record len %d\n", rd[i].data_size);
192 if (rd[i].record_type == rh->query->type)
194 answer_records[i].name = rh->query->name;
195 answer_records[i].type = rd[i].record_type;
196 answer_records[i].data.raw.data_len = rd[i].data_size;
197 answer_records[i].data.raw.data = (char*)rd[i].data;
198 answer_records[i].expiration_time = rd[i].expiration;
199 answer_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
203 additional_records[i].name = rh->query->name;
204 additional_records[i].type = rd[i].record_type;
205 additional_records[i].data.raw.data_len = rd[i].data_size;
206 additional_records[i].data.raw.data = (char*)rd[i].data;
207 additional_records[i].expiration_time = rd[i].expiration;
208 additional_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
212 packet->num_answers = rh->answered;
213 packet->num_additional_records = rd_count-(rh->answered);
215 if (0 == GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash))
216 packet->flags.authoritative_answer = 1;
218 packet->flags.authoritative_answer = 0;
221 packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NAME_ERROR;
223 packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
225 packet->flags.query_or_response = 1;
231 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
232 "Building DNS response\n");
233 ret = GNUNET_DNSPARSER_pack (packet,
234 1024, /* FIXME magic from dns redirector */
237 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
238 "Built DNS response! (ret=%d,len=%d)\n", ret, len);
239 if (ret == GNUNET_OK)
241 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
242 "Answering DNS request\n");
243 GNUNET_DNS_request_answer(rh->request_handle,
246 //GNUNET_free(answer);
247 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Answered DNS request\n");
251 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
252 "Error building DNS response! (ret=%d)", ret);
255 GNUNET_free(rh->name);
261 * Task run during shutdown.
267 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
269 /* Kill zone task for it may make the scheduler hang */
270 if (zone_update_taskid)
271 GNUNET_SCHEDULER_cancel(zone_update_taskid);
273 GNUNET_DNS_disconnect(dns_handle);
274 GNUNET_NAMESTORE_disconnect(namestore_handle, 1);
275 GNUNET_DHT_disconnect(dht_handle);
279 * Callback when record data is put into namestore
281 * @param cls the closure
282 * @param success GNUNET_OK on success
283 * @param emsg the error message. NULL if SUCCESS==GNUNET_OK
286 on_namestore_record_put_result(void *cls,
290 if (GNUNET_NO == success)
292 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "records already in namestore\n");
295 else if (GNUNET_YES == success)
297 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
298 "records successfully put in namestore\n");
302 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
303 "Error putting records into namestore: %s\n", emsg);
307 * Handle timeout for DHT requests
309 * @param cls the request handle as closure
310 * @param tc the task context
313 dht_lookup_timeout(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
315 struct GNUNET_GNS_ResolverHandle *rh = cls;
317 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
318 "dht lookup for query %s (type=%d) timed out.\n",
319 rh->name, rh->query->type);
321 GNUNET_DHT_get_stop (rh->get_handle);
322 reply_to_dns(rh, 0, NULL);
328 * Function called when we get a result from the dht
331 * @param cls the request handle
332 * @param exp lifetime
333 * @param key the key the record was stored under
334 * @param get_path get path
335 * @param get_path_length get path length
336 * @param put_path put path
337 * @param put_path_length put path length
338 * @param type the block type
339 * @param size the size of the record
340 * @param data the record data
343 process_record_dht_result(void* cls,
344 struct GNUNET_TIME_Absolute exp,
345 const GNUNET_HashCode * key,
346 const struct GNUNET_PeerIdentity *get_path,
347 unsigned int get_path_length,
348 const struct GNUNET_PeerIdentity *put_path,
349 unsigned int put_path_length,
350 enum GNUNET_BLOCK_Type type,
351 size_t size, const void *data)
353 struct GNUNET_GNS_ResolverHandle *rh;
354 struct GNSNameRecordBlock *nrb;
355 uint32_t num_records;
357 char* rd_data = (char*)data;
361 GNUNET_HashCode zone, name_hash;
362 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "got dht result (size=%d)\n", size);
367 //FIXME maybe check expiration here, check block type
369 rh = (struct GNUNET_GNS_ResolverHandle *)cls;
370 nrb = (struct GNSNameRecordBlock*)data;
372 /* stop lookup and timeout task */
373 GNUNET_DHT_get_stop (rh->get_handle);
374 GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
376 rh->get_handle = NULL;
377 name = (char*)&nrb[1];
378 num_records = ntohl(nrb->rd_count);
380 struct GNUNET_NAMESTORE_RecordData rd[num_records];
382 rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
383 rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
385 if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
390 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
394 for (i=0; i<num_records; i++)
396 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
397 "Got name: %s (wanted %s)\n", name, rh->name);
398 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
399 "Got type: %d (wanted %d)\n",
400 rd[i].record_type, rh->query->type);
401 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
402 "Got data length: %d\n", rd[i].data_size);
403 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
404 "Got flag %d\n", rd[i].flags);
406 if ((strcmp(name, rh->name) == 0) &&
407 (rd[i].record_type == rh->query->type))
414 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
415 GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
418 * FIXME check pubkey against existing key in namestore?
419 * https://gnunet.org/bugs/view.php?id=2179
422 /* Save to namestore */
423 GNUNET_NAMESTORE_record_put (namestore_handle,
430 &on_namestore_record_put_result, //cont
434 reply_to_dns(rh, num_records, rd);
436 reply_to_dns(rh, 0, NULL);
443 * Start DHT lookup for a (name -> query->record_type) record in
444 * query->authority's zone
446 * @param rh the pending gns query context
447 * @param name the name to query record
450 resolve_record_dht(struct GNUNET_GNS_ResolverHandle *rh)
453 GNUNET_HashCode name_hash;
454 GNUNET_HashCode lookup_key;
455 struct GNUNET_CRYPTO_HashAsciiEncoded lookup_key_string;
457 GNUNET_CRYPTO_hash(rh->name, strlen(rh->name), &name_hash);
458 GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
459 GNUNET_CRYPTO_hash_to_enc (&lookup_key, &lookup_key_string);
461 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
462 "starting dht lookup for %s with key: %s\n",
463 rh->name, (char*)&lookup_key_string);
465 rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed(DHT_LOOKUP_TIMEOUT,
466 &dht_lookup_timeout, rh);
468 xquery = htonl(rh->query->type);
469 rh->get_handle = GNUNET_DHT_get_start(dht_handle,
470 DHT_OPERATION_TIMEOUT,
471 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
473 DHT_GNS_REPLICATION_LEVEL,
477 &process_record_dht_result,
484 * Namestore calls this function if we have record for this name.
485 * (or with rd_count=0 to indicate no matches)
487 * @param cls the pending query
488 * @param key the key of the zone we did the lookup
489 * @param expiration expiration date of the namestore entry
490 * @param name the name for which we need an authority
491 * @param rd_count the number of records with 'name'
492 * @param rd the record data
493 * @param signature the signature of the authority for the record data
496 process_record_lookup(void* cls,
497 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
498 struct GNUNET_TIME_Absolute expiration,
499 const char *name, unsigned int rd_count,
500 const struct GNUNET_NAMESTORE_RecordData *rd,
501 const struct GNUNET_CRYPTO_RsaSignature *signature)
503 struct GNUNET_GNS_ResolverHandle *rh;
504 struct GNUNET_TIME_Relative remaining_time;
505 GNUNET_HashCode zone;
507 rh = (struct GNUNET_GNS_ResolverHandle *) cls;
508 GNUNET_CRYPTO_hash(key,
509 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
511 remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
516 * Lookup terminated and no results
517 * -> DHT Phase unless data is recent
519 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
520 "Namestore lookup for %s terminated without results\n", name);
523 * Not our root and no record found. Try dht if expired
525 if ((0 != GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash)) &&
526 (remaining_time.rel_value != 0))
528 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
529 "Record %s unknown in namestore, trying dht\n",
531 resolve_record_dht(rh);
536 * Our zone and no result? Cannot resolve TT
538 GNUNET_assert(rh->answered == 0);
539 reply_to_dns(rh, 0, NULL);
546 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
547 "Processing additional result %s from namestore\n", name);
549 for (i=0; i<rd_count;i++)
552 if (rd[i].record_type != rh->query->type)
555 if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
558 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "This record is expired. Skipping\n");
569 if (rh->answered == 0)
571 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
572 "No answers found. This is odd!\n");
573 reply_to_dns(rh, 0, NULL);
577 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Found %d answer(s) to query!\n",
580 reply_to_dns(rh, rd_count, rd);
586 * The final phase of resolution.
587 * This is a name that is canonical and we do not have a delegation.
589 * @param rh the pending lookup
592 resolve_record(struct GNUNET_GNS_ResolverHandle *rh)
596 * Try to resolve this record in our namestore.
597 * The name to resolve is now in rh->authority_name
598 * since we tried to resolve it to an authority
601 GNUNET_NAMESTORE_lookup_record(namestore_handle,
605 &process_record_lookup,
612 * Handle timeout for DHT requests
614 * @param cls the request handle as closure
615 * @param tc the task context
618 dht_authority_lookup_timeout(void *cls,
619 const struct GNUNET_SCHEDULER_TaskContext *tc)
621 struct GNUNET_GNS_ResolverHandle *rh = cls;
623 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
624 "dht lookup for query %s (type=%d) timed out.\n",
625 rh->name, rh->query->type);
627 if (strcmp(rh->name, "") == 0)
630 * promote authority back to name and try to resolve record
632 strcpy(rh->name, rh->authority_name);
637 GNUNET_DHT_get_stop (rh->get_handle);
638 reply_to_dns(rh, 0, NULL);
644 * Function called when we get a result from the dht
647 * @param cls the request handle
648 * @param exp lifetime
649 * @param key the key the record was stored under
650 * @param get_path get path
651 * @param get_path_length get path length
652 * @param put_path put path
653 * @param put_path_length put path length
654 * @param type the block type
655 * @param size the size of the record
656 * @param data the record data
659 process_authority_dht_result(void* cls,
660 struct GNUNET_TIME_Absolute exp,
661 const GNUNET_HashCode * key,
662 const struct GNUNET_PeerIdentity *get_path,
663 unsigned int get_path_length,
664 const struct GNUNET_PeerIdentity *put_path,
665 unsigned int put_path_length,
666 enum GNUNET_BLOCK_Type type,
667 size_t size, const void *data)
669 struct GNUNET_GNS_ResolverHandle *rh;
670 struct GNSNameRecordBlock *nrb;
671 uint32_t num_records;
673 char* rd_data = (char*) data;
676 GNUNET_HashCode zone, name_hash;
681 //FIXME check expiration?
683 rh = (struct GNUNET_GNS_ResolverHandle *)cls;
684 nrb = (struct GNSNameRecordBlock*)data;
686 /* stop dht lookup and timeout task */
687 GNUNET_DHT_get_stop (rh->get_handle);
688 GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
690 rh->get_handle = NULL;
691 num_records = ntohl(nrb->rd_count);
692 name = (char*)&nrb[1];
694 struct GNUNET_NAMESTORE_RecordData rd[num_records];
696 rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
697 rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
699 if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
704 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
708 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
709 "Got name: %s (wanted %s)\n", name, rh->authority_name);
710 for (i=0; i<num_records; i++)
713 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
714 "Got name: %s (wanted %s)\n", name, rh->authority_name);
715 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
716 "Got type: %d (wanted %d)\n",
717 rd[i].record_type, GNUNET_GNS_RECORD_PKEY);
718 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
719 "Got data length: %d\n", rd[i].data_size);
720 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
721 "Got flag %d\n", rd[i].flags);
723 if ((strcmp(name, rh->authority_name) == 0) &&
724 (rd[i].record_type == GNUNET_GNS_RECORD_PKEY))
726 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Authority found in DHT\n");
729 (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *)rd[i].data,
737 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
738 GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
740 /* Save to namestore */
741 if (0 != GNUNET_CRYPTO_hash_cmp(&zone_hash, &zone))
743 GNUNET_NAMESTORE_record_put (namestore_handle,
750 &on_namestore_record_put_result, //cont
759 if (strcmp(rh->name, "") == 0)
767 if (strcmp(rh->name, "") == 0)
769 /* promote authority back to name */
770 strcpy(rh->name, rh->authority_name);
774 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "No authority in records\n");
775 reply_to_dns(rh, 0, NULL);
779 * Start DHT lookup for a name -> PKEY (compare NS) record in
780 * query->authority's zone
782 * @param rh the pending gns query
783 * @param name the name of the PKEY record
786 resolve_authority_dht(struct GNUNET_GNS_ResolverHandle *rh)
789 GNUNET_HashCode name_hash;
790 GNUNET_HashCode lookup_key;
792 GNUNET_CRYPTO_hash(rh->authority_name,
793 strlen(rh->authority_name),
795 GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
797 rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
798 &dht_authority_lookup_timeout,
801 xquery = htonl(GNUNET_GNS_RECORD_PKEY);
802 //FIXME how long to wait for results?
803 rh->get_handle = GNUNET_DHT_get_start(dht_handle,
804 DHT_OPERATION_TIMEOUT,
805 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
807 DHT_GNS_REPLICATION_LEVEL,
811 &process_authority_dht_result,
817 * This is a callback function that should give us only PKEY
818 * records. Used to query the namestore for the authority (PKEY)
821 * @param cls the pending query
822 * @param key the key of the zone we did the lookup
823 * @param expiration expiration date of the record data set in the namestore
824 * @param name the name for which we need an authority
825 * @param rd_count the number of records with 'name'
826 * @param rd the record data
827 * @param signature the signature of the authority for the record data
830 process_authority_lookup(void* cls,
831 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
832 struct GNUNET_TIME_Absolute expiration,
834 unsigned int rd_count,
835 const struct GNUNET_NAMESTORE_RecordData *rd,
836 const struct GNUNET_CRYPTO_RsaSignature *signature)
838 struct GNUNET_GNS_ResolverHandle *rh;
839 struct GNUNET_TIME_Relative remaining_time;
840 GNUNET_HashCode zone;
842 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got %d records from authority lookup\n",
845 rh = (struct GNUNET_GNS_ResolverHandle *)cls;
846 GNUNET_CRYPTO_hash(key,
847 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
849 remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
852 * No authority found in namestore.
857 * We did not find an authority in the namestore
858 * _IF_ the current authoritative zone is us we cannot resolve
859 * _ELSE_ we can still check the _expired_ dht
863 * No PKEY in our root. Try to resolve actual type in our zone
864 * if name is canonical. Else we cannot resolve.
866 if (0 == GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash))
868 if (strcmp(rh->name, "") == 0)
871 * Promote this authority back to a name
873 strcpy(rh->name, rh->authority_name);
879 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
880 "Authority %s unknown in namestore, cannot resolve\n",
883 reply_to_dns(rh, 0, NULL);
888 * Not our root and no PKEY found. Try dht if expired
889 * FIXME only do when expired?
891 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "AAAA %d\n", remaining_time.rel_value);
892 if ((0 != GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash)))
894 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
895 "Authority %s unknown in namestore, trying dht\n",
897 resolve_authority_dht(rh);
902 * Not our root and not expired or no records. Cannot resolve
904 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Authority %s unknown\n",
906 reply_to_dns(rh, 0, NULL);
910 //Note only 1 pkey should have been returned.. anything else would be strange
912 * We found an authority that may be able to help us
916 for (i=0; i<rd_count;i++)
919 if (rd[i].record_type != GNUNET_GNS_RECORD_PKEY)
922 if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
925 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "This pkey is expired.\n");
926 if (remaining_time.rel_value == 0)
928 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
929 "This dht entry is expired. Refreshing\n");
930 resolve_authority_dht(rh);
938 * Resolve rest of query with new authority
940 GNUNET_assert(rd[i].record_type == GNUNET_GNS_RECORD_PKEY);
941 GNUNET_CRYPTO_hash(rd[i].data,
942 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
944 if (strcmp(rh->name, "") == 0)
954 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
955 "Authority lookup successful but no PKEY... never get here\n");
956 reply_to_dns(rh, 0, NULL);
961 * Determine if this name is canonical.
963 * a.b.gnunet = not canonical
966 * @param name the name to test
967 * @return 1 if canonical
970 is_canonical(char* name)
972 uint32_t len = strlen(name);
975 for (i=0; i<len; i++)
977 if (*(name+i) == '.')
984 * Move one level up in the domain hierarchy and return the
985 * passed top level domain.
987 * @param name the domain
988 * @param dest the destination where the tld will be put
991 pop_tld(char* name, char* dest)
995 if (is_canonical(name))
1002 for (len = strlen(name); len > 0; len--)
1004 if (*(name+len) == '.')
1014 strcpy(dest, (name+len+1));
1019 * The first phase of resolution.
1020 * First check if the name is canonical.
1021 * If it is then try to resolve directly.
1022 * If not then we first have to resolve the authoritative entities.
1024 * @param rh the pending lookup
1027 resolve_name(struct GNUNET_GNS_ResolverHandle *rh)
1031 * Try to resolve this name to a delegation.
1033 pop_tld(rh->name, rh->authority_name);
1034 GNUNET_NAMESTORE_lookup_record(namestore_handle,
1037 GNUNET_GNS_RECORD_PKEY,
1038 &process_authority_lookup,
1044 * Entry point for name resolution
1045 * Setup a new query and try to resolve
1047 * @param request the request handle of the DNS request from a client
1048 * @param p the DNS query packet we received
1049 * @param q the DNS query we received parsed from p
1052 start_resolution(struct GNUNET_DNS_RequestHandle *request,
1053 struct GNUNET_DNSPARSER_Packet *p,
1054 struct GNUNET_DNSPARSER_Query *q)
1056 struct GNUNET_GNS_ResolverHandle *rh;
1058 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1059 "Starting resolution for %s (type=%d)!\n",
1062 rh = GNUNET_malloc(sizeof (struct GNUNET_GNS_ResolverHandle));
1065 rh->authority = zone_hash;
1067 rh->name = GNUNET_malloc(strlen(q->name)
1068 - strlen(gnunet_tld) + 1);
1070 strlen(q->name)-strlen(gnunet_tld) + 1);
1071 memcpy(rh->name, q->name,
1072 strlen(q->name)-strlen(gnunet_tld));
1074 rh->authority_name = GNUNET_malloc(sizeof(char)*MAX_DNS_LABEL_LENGTH);
1076 rh->request_handle = request;
1078 /* Start resolution in our zone */
1083 * The DNS request handler
1084 * Called for every incoming DNS request.
1086 * @param cls closure
1087 * @param rh request handle to user for reply
1088 * @param request_length number of bytes in request
1089 * @param request udp payload of the DNS request
1092 handle_dns_request(void *cls,
1093 struct GNUNET_DNS_RequestHandle *rh,
1094 size_t request_length,
1095 const char *request)
1097 struct GNUNET_DNSPARSER_Packet *p;
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hijacked a DNS request...processing\n");
1101 p = GNUNET_DNSPARSER_parse (request, request_length);
1105 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1106 "Received malformed DNS packet, leaving it untouched\n");
1107 GNUNET_DNS_request_forward (rh);
1112 * Check tld and decide if we or
1113 * legacy dns is responsible
1115 * FIXME now in theory there could be more than 1 query in the request
1116 * but if this is case we get into trouble:
1117 * either we query the GNS or the DNS. We cannot do both!
1118 * So I suggest to either only allow a single query per request or
1119 * only allow GNS or DNS requests.
1120 * The way it is implemented here now is buggy and will lead to erratic
1121 * behaviour (if multiple queries are present).
1123 if (p->num_queries == 0)
1125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126 "No Queries in DNS packet... forwarding\n");
1127 GNUNET_DNS_request_forward (rh);
1130 if (p->num_queries > 1)
1132 /* Note: We could also look for .gnunet */
1133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1134 ">1 queriy in DNS packet... odd. We only process #1\n");
1141 tldoffset = p->queries[0].name + strlen(p->queries[0].name);
1143 while ((*tldoffset) != '.')
1146 if (0 == strcmp(tldoffset, gnunet_tld))
1148 start_resolution(rh, p, p->queries);
1153 * This request does not concern us. Forward to real DNS.
1155 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1156 "Request for %s is forwarded to DNS\n", p->queries[0].name);
1157 GNUNET_DNS_request_forward (rh);
1163 * Method called periodicattluy that triggers
1164 * iteration over root zone
1166 * @param cls closure
1167 * @param tc task context
1170 update_zone_dht_next(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1172 GNUNET_NAMESTORE_zone_iterator_next(namestore_iter);
1176 * Continuation for DHT put
1178 * @param cls closure
1179 * @param tc task context
1182 record_dht_put(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1184 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "put request transmitted\n");
1189 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1192 * Function used to put all records successively into the DHT.
1194 * @param cls the closure (NULL)
1195 * @param key the public key of the authority (ours)
1196 * @param expiration lifetime of the namestore entry
1197 * @param name the name of the records
1198 * @param rd_count the number of records in data
1199 * @param rd the record data
1200 * @param signature the signature for the record data
1203 put_gns_record(void *cls,
1204 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
1205 struct GNUNET_TIME_Absolute expiration,
1207 unsigned int rd_count,
1208 const struct GNUNET_NAMESTORE_RecordData *rd,
1209 const struct GNUNET_CRYPTO_RsaSignature *signature)
1212 struct GNSNameRecordBlock *nrb;
1213 GNUNET_HashCode name_hash;
1214 GNUNET_HashCode xor_hash;
1215 struct GNUNET_CRYPTO_HashAsciiEncoded xor_hash_string;
1216 uint32_t rd_payload_length;
1217 char* nrb_data = NULL;
1222 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Zone iteration finished\n");
1223 GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
1224 zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_start,
1229 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1230 "Putting records for %s into the DHT\n", name);
1232 rd_payload_length = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
1234 nrb = GNUNET_malloc(rd_payload_length + strlen(name) + 1
1235 + sizeof(struct GNSNameRecordBlock));
1237 if (signature != NULL)
1238 nrb->signature = *signature;
1240 nrb->public_key = *key;
1242 nrb->rd_count = htonl(rd_count);
1244 memset(&nrb[1], 0, strlen(name) + 1);
1245 memcpy(&nrb[1], name, strlen(name));
1247 nrb_data = (char*)&nrb[1];
1248 nrb_data += strlen(name) + 1;
1250 rd_payload_length += sizeof(struct GNSNameRecordBlock) +
1253 if (-1 == GNUNET_NAMESTORE_records_serialize (rd_count,
1258 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Record serialization failed!\n");
1265 * calculate DHT key: H(name) xor H(pubkey)
1267 GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
1268 GNUNET_CRYPTO_hash_xor(&zone_hash, &name_hash, &xor_hash);
1269 GNUNET_CRYPTO_hash_to_enc (&xor_hash, &xor_hash_string);
1270 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1271 "putting records for %s under key: %s with size %d\n",
1272 name, (char*)&xor_hash_string, rd_payload_length);
1274 GNUNET_DHT_put (dht_handle, &xor_hash,
1275 DHT_GNS_REPLICATION_LEVEL,
1277 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
1281 DHT_OPERATION_TIMEOUT,
1283 NULL); //cls for cont
1285 num_public_records++;
1288 * Reschedule periodic put
1290 zone_update_taskid = GNUNET_SCHEDULER_add_delayed (dht_update_interval,
1291 &update_zone_dht_next,
1297 * Periodically iterate over our zone and store everything in dht
1300 * @param tc task context
1303 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1305 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting DHT zone update!\n");
1306 if (0 == num_public_records)
1308 dht_update_interval = GNUNET_TIME_relative_multiply(
1309 GNUNET_TIME_UNIT_SECONDS,
1314 dht_update_interval = GNUNET_TIME_relative_multiply(
1315 GNUNET_TIME_UNIT_SECONDS,
1316 (3600/num_public_records));
1318 num_public_records = 0; //start counting again
1319 namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
1321 GNUNET_NAMESTORE_RF_AUTHORITY,
1322 GNUNET_NAMESTORE_RF_PRIVATE,
1327 typedef void (*ShortenResponseProc) (void* cls, const char* name);
1330 * Shorten a given name
1332 * @param name the name to shorten
1333 * @param proc the processor to call when finished
1334 * @praram cls the closure to the processor
1337 shorten_name(char* name, ShortenResponseProc proc, void* cls)
1339 char* result = name;
1344 * Send shorten response back to client
1346 * @param cls the client handle in closure
1347 * @param name the shortened name result or NULL if cannot be shortened
1350 send_shorten_response(void* cls, const char* name)
1352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message\n",
1354 struct ClientShortenHandle *csh = (struct ClientShortenHandle *)cls;
1355 struct GNUNET_GNS_ClientShortenResultMessage *rmsg;
1362 rmsg = GNUNET_malloc(sizeof(struct GNUNET_GNS_ClientShortenResultMessage *)
1365 rmsg->unique_id = csh->unique_id;
1366 rmsg->key = csh->key;
1367 rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT);
1369 strcpy((char*)&rmsg[1], name);
1371 GNUNET_SERVER_notification_context_unicast (nc, csh->client,
1372 (const struct GNUNET_MessageHeader *) &rmsg,
1375 GNUNET_SERVER_receive_done (csh->client, GNUNET_OK);
1383 * Handle a shorten message from the api
1385 * @param cls the closure
1386 * @param client the client
1387 * @param message the message
1389 static void handle_shorten(void *cls,
1390 struct GNUNET_SERVER_Client * client,
1391 const struct GNUNET_MessageHeader * message)
1393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "SHORTEN");
1395 size_t msg_size = 0;
1396 struct ClientShortenHandle *csh;
1398 if (ntohs (message->size) != sizeof (struct GNUNET_GNS_ClientShortenMessage))
1400 GNUNET_break_op (0);
1401 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1405 struct GNUNET_GNS_ClientShortenMessage *sh_msg =
1406 (struct GNUNET_GNS_ClientShortenMessage *) message;
1408 msg_size = ntohs(message->size);
1410 if (msg_size > GNUNET_SERVER_MAX_MESSAGE_SIZE)
1412 GNUNET_break_op (0);
1413 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1417 csh = GNUNET_malloc(sizeof(struct ClientShortenHandle));
1418 csh->client = client;
1419 csh->unique_id = sh_msg->unique_id;
1420 csh->key = sh_msg->key;
1423 shorten_name((char*)&sh_msg[1], &send_shorten_response, csh);
1431 handle_lookup(void *cls,
1432 struct GNUNET_SERVER_Client * client,
1433 const struct GNUNET_MessageHeader * message)
1438 * Process GNS requests.
1440 * @param cls closure)
1441 * @param server the initialized server
1442 * @param c configuration to use
1445 run (void *cls, struct GNUNET_SERVER_Handle *server,
1446 const struct GNUNET_CONFIGURATION_Handle *c)
1449 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Initializing GNS\n");
1452 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1454 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1455 {&handle_shorten, NULL, GNUNET_MESSAGE_TYPE_GNS_SHORTEN, 0},
1456 {&handle_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_LOOKUP, 0}
1459 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "gns",
1460 "ZONEKEY", &keyfile))
1462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1463 "No private key for root zone specified%s!\n", keyfile);
1464 GNUNET_SCHEDULER_shutdown(0);
1468 zone_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1469 GNUNET_CRYPTO_rsa_key_get_public (zone_key, &pkey);
1471 GNUNET_CRYPTO_hash(&pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1476 GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1479 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1480 "DNS hijacking enabled... connecting to service.\n");
1482 * Do gnunet dns init here
1484 dns_handle = GNUNET_DNS_connect(c,
1485 GNUNET_DNS_FLAG_PRE_RESOLUTION,
1486 &handle_dns_request, /* rh */
1487 NULL); /* Closure */
1488 if (NULL == dns_handle)
1490 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1491 "Failed to connect to the dnsservice!\n");
1498 * handle to our local namestore
1500 namestore_handle = GNUNET_NAMESTORE_connect(c);
1502 if (NULL == namestore_handle)
1504 //FIXME do error handling;
1505 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1506 "Failed to connect to the namestore!\n");
1507 GNUNET_SCHEDULER_shutdown(0);
1514 dht_handle = GNUNET_DHT_connect(c, 1); //FIXME get ht_len from cfg
1516 if (NULL == dht_handle)
1518 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
1521 //put_some_records(); //FIXME for testing
1524 * Schedule periodic put
1526 * We have roughly an hour for all records;
1528 dht_update_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
1530 //zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_start, NULL);
1532 GNUNET_SERVER_add_handlers (server, handlers);
1535 //GNUNET_SERVER_disconnect_notify (server,
1536 // &client_disconnect_notification,
1539 nc = GNUNET_SERVER_notification_context_create (server, 1);
1541 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1547 * The main function for the GNS service.
1549 * @param argc number of arguments from the command line
1550 * @param argv command line arguments
1551 * @return 0 ok, 1 on error
1554 main (int argc, char *const *argv)
1560 GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
1565 /* end of gnunet-service-gns.c */