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.
25 * - Think about mixed dns queries (.gnunet and .org)
26 * - The smaller FIXME issues all around
28 * @file gns/gnunet-service-gns.c
29 * @brief GNUnet GNS service
30 * @author Martin Schanzenbach
33 #include "gnunet_util_lib.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_dns_service.h"
36 #include "gnunet_dnsparser_lib.h"
37 #include "gnunet_dht_service.h"
38 #include "gnunet_namestore_service.h"
39 #include "gnunet_gns_service.h"
42 /* Ignore for now not used anyway and probably never will */
43 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP 23
44 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT 24
46 struct GNUNET_GNS_QueryRecordList
51 struct GNUNET_GNS_QueryRecordList * next;
52 struct GNUNET_GNS_QueryRecordList * prev;
54 struct GNUNET_DNSPARSER_Record * record;
58 * A result list for namestore queries
60 struct GNUNET_GNS_PendingQuery
62 /* the answer packet */
63 struct GNUNET_DNSPARSER_Packet *answer;
65 /* records to put into answer packet */
66 struct GNUNET_GNS_QueryRecordList * records_head;
67 struct GNUNET_GNS_QueryRecordList * records_tail;
70 int num_authority_records; //FIXME are all of our replies auth?
74 /* the dns request id */
75 int id; // FIXME can handle->request_id also be used here?
77 /* the request handle to reply to */
78 struct GNUNET_DNS_RequestHandle *request_handle;
80 /* hast this query been answered? */
83 /* we have an authority in namestore that
84 * may be able to resolve
91 * Our handle to the DNS handler library
93 struct GNUNET_DNS_Handle *dns_handle;
96 * Our handle to the DHT
98 struct GNUNET_DHT_Handle *dht_handle;
100 struct GNUNET_TIME_Relative dht_update_interval;
104 * FIXME get the real deal
106 struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
109 * Our handle to the namestore service
111 struct GNUNET_NAMESTORE_Handle *namestore_handle;
114 * The configuration the GNS service is running with
116 const struct GNUNET_CONFIGURATION_Handle *GNS_cfg;
119 * Our notification context.
121 static struct GNUNET_SERVER_NotificationContext *nc;
126 GNUNET_HashCode *zone_hash;
129 * Task run during shutdown.
135 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
137 GNUNET_DNS_disconnect(dns_handle);
138 GNUNET_NAMESTORE_disconnect(namestore_handle, 0);
139 GNUNET_DHT_disconnect(dht_handle);
144 * This is where it gets tricky
145 * First we store (cache) all replies. Simple.
146 * If we see an authority "closer" to the name
147 * we have to start a new query. Unless we get
149 * It is important that the authority is closer
150 * because else we might end up in an endless loop
151 * (maybe keep track of queried keys?)
152 * Of course we could just limit the resolution
153 * with a timeout (makes sense for clients) but we need
154 * to know when to stop querying.
157 handle_dht_reply(void* cls,
158 struct GNUNET_TIME_Absolute exp,
159 const GNUNET_HashCode * key,
160 const struct GNUNET_PeerIdentity *get_path,
161 unsigned int get_path_length,
162 const struct GNUNET_PeerIdentity *put_path,
163 unsigned int put_path_length,
164 enum GNUNET_BLOCK_Type type,
165 size_t size, const void *data)
170 process_auth_query(void* cls, const GNUNET_HashCode *zone,
171 const char *name, uint32_t record_type,
172 struct GNUNET_TIME_Absolute expiration,
173 enum GNUNET_NAMESTORE_RecordFlags flags,
174 const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
175 size_t size, const void *data)
177 struct GNUNET_GNS_PendingQuery *query;
179 query = (struct GNUNET_GNS_PendingQuery *)cls;
183 * FIXME We assume this will never return our authority
185 if ((NULL == data) && !query->authority_found)
187 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Find authority\n");
188 if (0 == strcmp(name, "gnunet"))
190 // Reached tld return nx
191 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "NX record\n");
195 //Move name to next level
196 while ((*name) != '.')
200 GNUNET_NAMESTORE_lookup_name(namestore_handle,
203 GNUNET_GNS_RECORD_PKEY,
210 * We found a PKEY that may be able to help us
212 query->authority_found = 1;
213 GNUNET_HashCode *key = (GNUNET_HashCode*) data;
216 struct GNUNET_TIME_Relative timeout =
217 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20);
218 GNUNET_DHT_get_start (dht_handle,
220 GNUNET_BLOCK_TYPE_TEST, //TODO our plugin
224 query->name, //xquery FIXME nobody will know this name
234 * Phase 2 of resolution.
237 lookup_dht(struct GNUNET_GNS_PendingQuery *query)
240 * In this phase we first want to find the
241 * responsible authority in the namestore
243 GNUNET_NAMESTORE_lookup_name(namestore_handle,
246 GNUNET_GNS_RECORD_PKEY,
252 reply_to_dns(struct GNUNET_GNS_PendingQuery *answer)
254 struct GNUNET_GNS_QueryRecordList *i;
255 struct GNUNET_DNSPARSER_Packet *packet;
256 struct GNUNET_DNSPARSER_Flags dnsflags;
262 packet = GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Packet));
264 GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Record) * answer->num_records);
266 len = sizeof(struct GNUNET_DNSPARSER_Record*);
268 for (i=answer->records_head; i != NULL; i=i->next)
270 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
271 "Adding %s to DNS response\n", i->record->name);
272 memcpy(&packet->answers[j],
274 sizeof (struct GNUNET_DNSPARSER_Record));
275 GNUNET_free(i->record);
278 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "after memcpy\n");
279 /* FIXME how to handle auth, additional etc */
280 packet->num_answers = answer->num_records;
281 packet->num_authority_records = answer->num_authority_records;
283 dnsflags.authoritative_answer = 1;
284 dnsflags.opcode = GNUNET_DNSPARSER_OPCODE_QUERY;
285 dnsflags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR; //not sure
286 dnsflags.query_or_response = 1;
287 packet->flags = dnsflags;
289 packet->id = answer->id;
290 ret = GNUNET_DNSPARSER_pack (packet,
291 1024, /* FIXME magic from dns redirector */
294 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
295 "Built DNS response! (ret=%d)\n", ret);
296 if (ret == GNUNET_OK)
298 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
299 "Answering DNS request\n");
300 GNUNET_DNS_request_answer(answer->request_handle,
304 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Answered DNS request\n");
305 //FIXME return code, free datastructures
309 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
310 "Error building DNS response! (ret=%d)", ret);
315 process_ns_result(void* cls, const GNUNET_HashCode *zone,
316 const char *name, uint32_t record_type,
317 struct GNUNET_TIME_Absolute expiration,
318 enum GNUNET_NAMESTORE_RecordFlags flags,
319 const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
320 size_t size, const void *data)
322 struct GNUNET_GNS_PendingQuery *query;
323 struct GNUNET_GNS_QueryRecordList *qrecord;
324 struct GNUNET_DNSPARSER_Record *record;
325 query = (struct GNUNET_GNS_PendingQuery *) cls;
331 * Last result received (or none)
332 * Do we have what we need to answer?
333 * If not -> DHT Phase
336 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
337 "Namestore lookup terminated. (answered=%d)", query->answered);
342 lookup_dht(query); //TODO
350 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
351 "Processing additional result %s from namestore\n", name);
353 qrecord = GNUNET_malloc(sizeof(struct GNUNET_GNS_QueryRecordList));
354 record = GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Record));
355 qrecord->record = record;
357 record->name = (char*)name;
358 /* FIXME for gns records this requires the dnsparser to be modified!
360 * maybe store record data appropriately in namestore to avoid this
363 if (record_type == GNUNET_DNSPARSER_TYPE_A)
365 record->data.raw.data = (char*)data;
366 record->data.raw.data_len = size;
368 record->expiration_time = expiration;
369 record->type = record_type;
370 record->class = GNUNET_DNSPARSER_CLASS_INTERNET; /* srsly? */
372 if (flags == GNUNET_NAMESTORE_RF_AUTHORITY)
374 //query->num_authority_records++;
377 if ((0 == strcmp(query->name , name)) &&
378 (query->type == record_type))
380 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Found answer to query!\n");
384 query->num_records++;
386 //FIXME watch for leaks
387 GNUNET_CONTAINER_DLL_insert(query->records_head,
395 * Phase 1 of name resolution: Lookup local namestore
397 * @param name the name to look up
398 * @param id the id of the dns request (for the reply)
399 * @param type the record type to look for
402 lookup_namestore(struct GNUNET_DNS_RequestHandle *rh,
403 char* name, uint16_t id, uint16_t type)
405 struct GNUNET_GNS_PendingQuery *answer;
408 * Do db lookup here. Make dht lookup if necessary
409 * FIXME for now only local lookups for our zone!
411 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "This is .gnunet (%s)!\n", name);
412 answer = GNUNET_malloc(sizeof (struct GNUNET_GNS_PendingQuery));
416 answer->request_handle = rh;
418 GNUNET_NAMESTORE_lookup_name(namestore_handle,
427 * The DNS request handler
430 * @param rh request handle to user for reply
431 * @param request_length number of bytes in request
432 * @param request udp payload of the DNS request
435 handle_dns_request(void *cls,
436 struct GNUNET_DNS_RequestHandle *rh,
437 size_t request_length,
441 * parse request for tld
443 struct GNUNET_DNSPARSER_Packet *p;
448 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "hijacked a request...processing\n");
449 p = GNUNET_DNSPARSER_parse (request, request_length);
453 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454 "Received malformed DNS packet, leaving it untouched\n");
455 GNUNET_DNS_request_forward (rh);
460 * Check tld and decide if we or
461 * legacy dns is responsible
463 for (i=0;i<p->num_queries;i++)
465 namelen = strlen(p->queries[i].name);
467 if (namelen < 7) /* this can't be .gnunet */
470 * Move our tld/root to config file
471 * Generate fake DNS reply that replaces .gnunet with .org for testing?
473 tail = p->queries[i].name+(namelen-7);
474 if (0 == strcmp(tail, ".gnunet"))
476 /* FIXME we need to answer to ALL queries in ONE response...
477 * What happens if some requests should be handled by us and
479 * Like this we only answer one...
481 lookup_namestore(rh, p->queries[i].name, p->id, p->queries[i].type);
486 * This request does not concern us. Forward to real DNS.
488 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
489 "Request for %s is forwarded to DNS\n", p->queries[i].name);
490 GNUNET_DNS_request_forward (rh);
497 handle_client_record_lookup(void *cls,
498 struct GNUNET_SERVER_Client *client,
499 const struct GNUNET_MessageHeader *message)
507 put_some_records(void)
509 /* put a few records into namestore */
510 char* ipA = "1.2.3.4";
511 char* ipB = "5.6.7.8";
512 struct in_addr *alice = GNUNET_malloc(sizeof(struct in_addr));
513 struct in_addr *bob = GNUNET_malloc(sizeof(struct in_addr));
514 GNUNET_assert(1 == inet_pton (AF_INET, ipA, alice));
515 GNUNET_assert(1 == inet_pton (AF_INET, ipB, bob));
516 GNUNET_NAMESTORE_record_put (namestore_handle,
519 GNUNET_GNS_RECORD_TYPE_A,
520 GNUNET_TIME_absolute_get_forever(),
521 GNUNET_NAMESTORE_RF_AUTHORITY,
523 sizeof(struct in_addr),
527 GNUNET_NAMESTORE_record_put (namestore_handle,
530 GNUNET_GNS_RECORD_TYPE_A,
531 GNUNET_TIME_absolute_get_forever(),
532 GNUNET_NAMESTORE_RF_AUTHORITY,
534 sizeof(struct in_addr),
540 //Prototype... needed in put function
542 update_zone_dht(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
545 put_gns_record(void *cls, const GNUNET_HashCode *zone, const char *name,
546 uint32_t record_type, struct GNUNET_TIME_Absolute expiration,
547 enum GNUNET_NAMESTORE_RecordFlags flags,
548 const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
549 size_t size, const void *record_data)
551 struct GNUNET_TIME_Relative timeout;
555 struct GNUNET_TIME_AbsoluteNBO exp_nbo;
556 exp_nbo = GNUNET_TIME_absolute_hton (expiration);
557 uint32_t namelen = htonl(strlen(name));
558 uint16_t flags_nbo = htons(flags);
559 uint64_t offset = GNUNET_htonll(sig_loc->offset);
560 uint32_t depth = htonl(sig_loc->depth);
561 uint32_t revision = htonl(sig_loc->revision);
564 * I guess this can be done prettier
566 size_t record_len = sizeof(size_t) + sizeof(uint32_t) +
568 sizeof(struct GNUNET_NAMESTORE_SignatureLocation) +
569 sizeof(uint32_t) + strlen(name) + size;
571 record_type = htonl(record_type);
573 data = GNUNET_malloc(record_len);
577 memcpy(data_ptr, &namelen, sizeof(size_t));
578 data_ptr += sizeof(size_t);
580 memcpy(data_ptr, name, namelen);
583 memcpy(data_ptr, &record_type, sizeof(uint32_t));
584 data_ptr += sizeof(uint32_t);
586 memcpy(data_ptr, &exp_nbo, sizeof(struct GNUNET_TIME_AbsoluteNBO));
587 data_ptr += sizeof(struct GNUNET_TIME_AbsoluteNBO);
589 memcpy(data_ptr, &flags_nbo, sizeof(uint16_t));
590 data_ptr += sizeof(uint16_t);
592 memcpy(data_ptr, &offset, sizeof(uint64_t));
593 data_ptr += sizeof(uint64_t);
595 memcpy(data_ptr, &depth, sizeof(uint32_t));
596 data_ptr += sizeof(uint32_t);
598 memcpy(data_ptr, &revision, sizeof(uint32_t));
599 data_ptr += sizeof(uint32_t);
601 memcpy(data_ptr, &size, sizeof(uint32_t));
602 data_ptr += sizeof(uint32_t);
605 * FIXME note that this only works with raw data in nbo
606 * write helper function that converts properly and returns buffer
608 memcpy(data_ptr, record_data, size);
610 /*Doing this made me sad...*/
615 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20);
617 GNUNET_DHT_put (dht_handle, zone_hash,
620 GNUNET_BLOCK_TYPE_TEST, //FIXME todo
623 expiration, //FIXME from record makes sense?
625 NULL, //FIXME continuation needed? success check?
626 NULL); //cls for cont
631 GNUNET_SCHEDULER_add_delayed (dht_update_interval,
638 * Periodically iterate over our zone and store everything in dht
641 update_zone_dht(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
643 GNUNET_NAMESTORE_zone_transfer (namestore_handle, zone_hash,
649 * Process GNS requests.
652 * @param server the initialized server
653 * @param c configuration to use
656 run (void *cls, struct GNUNET_SERVER_Handle *server,
657 const struct GNUNET_CONFIGURATION_Handle *c)
660 /* The IPC message types */
661 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
662 /* callback, cls, type, size */
663 {&handle_client_record_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP,
668 zone_key = GNUNET_CRYPTO_rsa_key_create ();
669 GNUNET_CRYPTO_hash(zone_key, GNUNET_CRYPTO_RSA_KEY_LENGTH,//FIXME is this ok?
672 nc = GNUNET_SERVER_notification_context_create (server, 1);
674 /* FIXME - do some config parsing
675 * - Maybe only hijack dns if option is set (HIJACK_DNS=1)
678 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
681 * Do gnunet dns init here
683 dns_handle = GNUNET_DNS_connect(c,
684 GNUNET_DNS_FLAG_PRE_RESOLUTION,
685 &handle_dns_request, /* rh */
688 if (NULL == dns_handle)
690 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
691 "Failed to connect to the dnsservice!\n");
695 * handle to our local namestore
697 namestore_handle = GNUNET_NAMESTORE_connect(c);
699 if (NULL == namestore_handle)
701 //FIXME do error handling;
702 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
703 "Failed to connect to the namestore!\n");
709 dht_handle = GNUNET_DHT_connect(c, 1); //FIXME get ht_len from cfg
711 if (NULL == dht_handle)
713 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
716 dht_update_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
717 60); //FIXME from cfg
718 GNUNET_SCHEDULER_add_delayed (dht_update_interval,
724 GNUNET_SERVER_add_handlers (server, handlers);
726 * Esp the lookup would require to keep track of the clients' context
728 * GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
734 * The main function for the GNS service.
736 * @param argc number of arguments from the command line
737 * @param argv command line arguments
738 * @return 0 ok, 1 on error
741 main (int argc, char *const *argv)
747 GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
752 /* end of gnunet-service-gns.c */