-some dht stuff
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  *
23  * TODO:
24  *    - Finish dht lookup
25  *    - Think about mixed dns queries (.gnunet and .org)
26  *    - The smaller FIXME issues all around
27  *
28  * @file gns/gnunet-service-gns.c
29  * @brief GNUnet GNS service
30  * @author Martin Schanzenbach
31  */
32 #include "platform.h"
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"
40 #include "gns.h"
41
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
45
46 struct GNUNET_GNS_QueryRecordList
47 {
48   /**
49    * DLL
50    */
51   struct GNUNET_GNS_QueryRecordList * next;
52   struct GNUNET_GNS_QueryRecordList * prev;
53
54   struct GNUNET_DNSPARSER_Record * record;
55 };
56
57 /**
58  * A result list for namestore queries
59  */
60 struct GNUNET_GNS_PendingQuery
61 {
62   /* the answer packet */
63   struct GNUNET_DNSPARSER_Packet *answer;
64
65   /* records to put into answer packet */
66   struct GNUNET_GNS_QueryRecordList * records_head;
67   struct GNUNET_GNS_QueryRecordList * records_tail;
68
69   int num_records;
70   int num_authority_records; //FIXME are all of our replies auth?
71   
72   char *original_name;
73   char *name;
74
75   uint16_t type;
76   /* the dns request id */
77   int id; // FIXME can handle->request_id also be used here?
78
79   /* the request handle to reply to */
80   struct GNUNET_DNS_RequestHandle *request_handle;
81
82   /* hast this query been answered? */
83   int answered;
84
85   /* the authoritative zone to query */
86   GNUNET_HashCode *authority;
87
88   /* we have an authority in namestore that
89    * may be able to resolve
90    */
91   int authority_found;
92 };
93
94
95 /**
96  * Our handle to the DNS handler library
97  */
98 struct GNUNET_DNS_Handle *dns_handle;
99
100 /**
101  * Our handle to the DHT
102  */
103 struct GNUNET_DHT_Handle *dht_handle;
104
105 struct GNUNET_TIME_Relative dht_update_interval;
106
107 /**
108  * Our zone's private key
109  */
110 struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
111
112 /**
113  * Our handle to the namestore service
114  */
115 struct GNUNET_NAMESTORE_Handle *namestore_handle;
116
117 /**
118  * The configuration the GNS service is running with
119  */
120 const struct GNUNET_CONFIGURATION_Handle *GNS_cfg;
121
122 /**
123  * Our notification context.
124  */
125 static struct GNUNET_SERVER_NotificationContext *nc;
126
127 /**
128  * Our zone hash
129  */
130 GNUNET_HashCode *zone_hash;
131
132 /**
133  * Task run during shutdown.
134  *
135  * @param cls unused
136  * @param tc unused
137  */
138 static void
139 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
140 {
141   GNUNET_DNS_disconnect(dns_handle);
142   GNUNET_NAMESTORE_disconnect(namestore_handle, 0);
143   GNUNET_DHT_disconnect(dht_handle);
144 }
145
146 /**
147  * FIXME
148  * This is where it gets tricky
149  * 1. we store (cache) all replies. Simple.
150  * 2. If we see an authority "closer" to the name
151  * we have to start a new query. Unless we get
152  * a resolution.
153  * It is important that the authority is closer
154  * because else we might end up in an endless loop
155  * (maybe keep track of queried keys?)
156  * Of course we could just limit the resolution
157  * with a timeout (makes sense for clients) but we need
158  * to know when to stop querying.
159  * 3. Also the name returned for the record here will probably
160  * not match our name. How do we check this?
161  */
162 void
163 process_authority_dht_result(void* cls,
164                  struct GNUNET_TIME_Absolute exp,
165                  const GNUNET_HashCode * key,
166                  const struct GNUNET_PeerIdentity *get_path,
167                  unsigned int get_path_length,
168                  const struct GNUNET_PeerIdentity *put_path,
169                  unsigned int put_path_length,
170                  enum GNUNET_BLOCK_Type type,
171                  size_t size, const void *data)
172 {
173   if (data == NULL)
174     return;
175
176   /**
177    * data is a searialized PKEY record (probably)
178    * parse, put into namestore
179    * namestore zone hash is in query.
180    * Then adjust query->name and call resolve_name
181    * with new zone (the one just received)
182    */
183 }
184
185 /**
186  * Start DHT lookup for a name -> PKEY (compare NS) record in
187  * query->authority's zone
188  *
189  * @param query the pending gns query
190  * @param name the name of the PKEY record
191  */
192 void
193 resolve_authority_dht(struct GNUNET_GNS_PendingQuery *query, const char* name)
194 {
195   enum GNUNET_GNS_RecordType rtype = GNUNET_GNS_RECORD_PKEY;
196   struct GNUNET_TIME_Relative timeout;
197   GNUNET_HashCode name_hash;
198   GNUNET_HashCode lookup_key;
199
200   GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
201   GNUNET_CRYPTO_hash_xor(&name_hash, query->authority, &lookup_key);
202
203   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20);
204   
205   //FIXME how long to wait for results?
206   GNUNET_DHT_get_start(dht_handle, timeout,
207                        GNUNET_BLOCK_TYPE_TEST, //FIXME todo
208                        &lookup_key,
209                        5, //Replication level FIXME
210                        GNUNET_DHT_RO_NONE,
211                        &rtype, //xquery FIXME this is bad
212                        sizeof(GNUNET_GNS_RECORD_PKEY),
213                        &process_authority_dht_result,
214                        query);
215
216 }
217
218 void
219 process_name_dht_result(void* cls,
220                  struct GNUNET_TIME_Absolute exp,
221                  const GNUNET_HashCode * key,
222                  const struct GNUNET_PeerIdentity *get_path,
223                  unsigned int get_path_length,
224                  const struct GNUNET_PeerIdentity *put_path,
225                  unsigned int put_path_length,
226                  enum GNUNET_BLOCK_Type type,
227                  size_t size, const void *data)
228 {
229   if (data == NULL)
230     return;
231
232   /**
233    * data is a searialized GNS record of type
234    * query->record_type. Parse and put into namestore
235    * namestore zone hash is in query.
236    * Check if record type and name match in query and reply
237    * to dns!
238    */
239 }
240
241 /**
242  * Start DHT lookup for a name -> query->record_type record in
243  * query->authority's zone
244  *
245  * @param query the pending gns query
246  * @param name the name to query record
247  */
248 void
249 resolve_name_dht(struct GNUNET_GNS_PendingQuery *query, const char* name)
250 {
251   struct GNUNET_TIME_Relative timeout;
252   GNUNET_HashCode name_hash;
253   GNUNET_HashCode lookup_key;
254
255   GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
256   GNUNET_CRYPTO_hash_xor(&name_hash, query->authority, &lookup_key);
257
258   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20);
259   
260   //FIXME how long to wait for results?
261   GNUNET_DHT_get_start(dht_handle, timeout,
262                        GNUNET_BLOCK_TYPE_TEST, //FIXME todo
263                        &lookup_key,
264                        5, //Replication level FIXME
265                        GNUNET_DHT_RO_NONE,
266                        &query->type, //xquery
267                        sizeof(query->type),
268                        &process_name_dht_result,
269                        query);
270
271 }
272 /**
273  * This is a callback function that should give us only PKEY
274  * records. Used to iteratively query the namestore for 'closest'
275  * authority.
276  *
277  * @param cls the pending query
278  * @param zone our zone hash
279  * @param name the name for which we need an authority
280  * @param record_type the type of record (PKEY)
281  * @param expiration expiration date of the record
282  * @param flags namestore record flags
283  * @param sig_loc the location of the record in the signature tree
284  * @param size the size of the record
285  * @param data the record data
286  */
287 void
288 process_authority_lookup(void* cls, const GNUNET_HashCode *zone,
289                    const char *name, uint32_t record_type,
290                    struct GNUNET_TIME_Absolute expiration,
291                    enum GNUNET_NAMESTORE_RecordFlags flags,
292                    const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
293                    size_t size, const void *data)
294 {
295   struct GNUNET_GNS_PendingQuery *query;
296
297   query = (struct GNUNET_GNS_PendingQuery *)cls;
298   
299   /**
300    * No authority found in namestore.
301    */
302   if (NULL == data)
303   {
304     if (query->authority_found)
305     {
306       query->authority_found = 0;
307       //FIXME continue lookup
308       return;
309     }
310
311     /**
312      * We did not find an authority in the namestore
313      * _IF_ the current authoritative zone is not us. we can
314      * check the dht.
315      * _ELSE_ we cannot resolve
316      */
317     if (GNUNET_CRYPTO_hash_cmp(zone, zone_hash))
318     {
319       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "NX record\n");
320       //FIXME return NX answer
321       return;
322     }
323
324     resolve_authority_dht(query, name);
325     return;
326   }
327   
328   /**
329    * We found an authority that may be able to help us
330    * move on with query
331    */
332   query->authority_found = 1;
333   GNUNET_HashCode *key = (GNUNET_HashCode*) data; //FIXME i assume this works
334   query->authority = key;
335   
336 }
337
338
339 /**
340  * Reply to client with the result from our lookup.
341  *
342  * @param answer the pending query used in the lookup
343  */
344 void
345 reply_to_dns(struct GNUNET_GNS_PendingQuery *answer)
346 {
347   struct GNUNET_GNS_QueryRecordList *i;
348   struct GNUNET_DNSPARSER_Packet *packet;
349   struct GNUNET_DNSPARSER_Flags dnsflags;
350   int j;
351   size_t len;
352   int ret;
353   char *buf;
354   
355   packet = GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Packet));
356   packet->answers =
357     GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Record) * answer->num_records);
358   
359   len = sizeof(struct GNUNET_DNSPARSER_Record*);
360   j = 0;
361   for (i=answer->records_head; i != NULL; i=i->next)
362   {
363     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
364                "Adding %s to DNS response\n", i->record->name);
365     memcpy(&packet->answers[j], 
366            i->record,
367            sizeof (struct GNUNET_DNSPARSER_Record));
368     GNUNET_free(i->record);
369     j++;
370   }
371   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "after memcpy\n");
372   /* FIXME how to handle auth, additional etc */
373   packet->num_answers = answer->num_records;
374   packet->num_authority_records = answer->num_authority_records;
375
376   dnsflags.authoritative_answer = 1;
377   dnsflags.opcode = GNUNET_DNSPARSER_OPCODE_QUERY;
378   dnsflags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR; //not sure
379   dnsflags.query_or_response = 1;
380   packet->flags = dnsflags;
381
382   packet->id = answer->id;
383   
384   //FIXME this is silently discarded
385   ret = GNUNET_DNSPARSER_pack (packet,
386                                1024, /* FIXME magic from dns redirector */
387                                &buf,
388                                &len);
389   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
390              "Built DNS response! (ret=%d)\n", ret);
391   if (ret == GNUNET_OK)
392   {
393     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
394                "Answering DNS request\n");
395     GNUNET_DNS_request_answer(answer->request_handle,
396                               len,
397                               buf);
398     GNUNET_free(answer);
399     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Answered DNS request\n");
400     //FIXME return code, free datastructures
401   }
402   else
403   {
404     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
405                "Error building DNS response! (ret=%d)", ret);
406   }
407 }
408
409 /**
410  * Namestore calls this function if we have an entry for this name.
411  * (or data=null to indicate the lookup has finished)
412  *
413  * @param cls the pending query
414  * @param zone the zone of the lookup
415  * @param name the name looked up
416  * @param record_type the record type
417  * @param expiration lifetime of the record
418  * @param flags record flags
419  * @param sig_loc location of the record in the signature tree
420  * @param size the size of the record
421  * @param data the record data
422  */
423 static void
424 process_authoritative_result(void* cls, const GNUNET_HashCode *zone,
425                   const char *name, uint32_t record_type,
426                   struct GNUNET_TIME_Absolute expiration,
427                   enum GNUNET_NAMESTORE_RecordFlags flags,
428                   const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
429                   size_t size, const void *data)
430 {
431   struct GNUNET_GNS_PendingQuery *query;
432   struct GNUNET_GNS_QueryRecordList *qrecord;
433   struct GNUNET_DNSPARSER_Record *record;
434   query = (struct GNUNET_GNS_PendingQuery *) cls;
435
436
437   if (NULL == data)
438   {
439     /**
440      * FIXME
441      * Lookup terminated
442      * Do we have what we need to answer?
443      * If not -> DHT Phase
444      * if full_name == next_name and not anwered we cannot resolve
445      */
446     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
447                "Namestore lookup terminated. (answered=%d)", query->answered);
448     if (query->answered)
449     {
450       reply_to_dns(query);
451       return;
452     }
453
454     /**
455      * if this is not our zone we cannot rely on the namestore to be
456      * complete. -> Query DHT
457      */
458     if (!GNUNET_CRYPTO_hash_cmp(zone, zone_hash))
459     {
460       //FIXME todo
461       resolve_name_dht(query, name);
462       return;
463     }
464
465     /**
466      * Our zone and no result? Cannot resolve TT
467      * FIXME modify query to say NX
468      */
469     return;
470
471   }
472   else
473   {
474     /**
475      * Record found
476      */
477     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
478                "Processing additional result for %s from namestore\n", name);
479
480     qrecord = GNUNET_malloc(sizeof(struct GNUNET_GNS_QueryRecordList));
481     record = GNUNET_malloc(sizeof(struct GNUNET_DNSPARSER_Record));
482     qrecord->record = record;
483
484     record->name = (char*)query->original_name;
485
486     /**
487      * FIXME for gns records this requires the dnsparser to be modified!
488      * or use RAW. But RAW data need serialization!
489      * maybe store record data appropriately in namestore to avoid a
490      * huge switch statement?
491      */
492     if (record_type == GNUNET_DNSPARSER_TYPE_A)
493     {
494       record->data.raw.data = (char*)data;
495       record->data.raw.data_len = size;
496     }
497     record->expiration_time = expiration;
498     record->type = record_type;
499     record->class = GNUNET_DNSPARSER_CLASS_INTERNET; /* srsly? */
500     
501     //FIXME authoritative answer if we find a result in namestore
502     if (flags == GNUNET_NAMESTORE_RF_AUTHORITY)
503     {
504       //query->num_authority_records++;
505     }
506     
507     /**
508      * This seems to take into account that the result could
509      * be different in name and or record type...
510      * but to me this does not make sense
511      */
512     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Found answer to query!\n");
513     query->answered = 1;
514
515     query->num_records++;
516
517     /**
518      * FIXME watch for leaks
519      * properly free pendingquery when the time comes
520      */
521     GNUNET_CONTAINER_DLL_insert(query->records_head,
522                                 query->records_tail,
523                                 qrecord);
524   }
525 }
526
527 int
528 is_canonical(char* name)
529 {
530   return 0;
531 }
532
533 char* move_up(char* name)
534 {
535   return name;
536 }
537
538 void
539 resolve_name(struct GNUNET_GNS_PendingQuery *query, GNUNET_HashCode *zone)
540 {
541   if (is_canonical(query->name))
542   {
543     //We only need to check this zone's ns
544     GNUNET_NAMESTORE_lookup_name(namestore_handle,
545                                zone,
546                                query->name,
547                                query->type,
548                                &process_authoritative_result,
549                                query);
550   }
551   else
552   {
553     //We have to resolve the authoritative entity
554     char *new_authority = move_up(query->name);
555     GNUNET_NAMESTORE_lookup_name(namestore_handle,
556                                  zone,
557                                  new_authority,
558                                  GNUNET_GNS_RECORD_PKEY,
559                                  &process_authority_lookup,
560                                  query);
561   }
562 }
563
564 /**
565  * Phase 1 of name resolution
566  * Lookup local namestore. If we find a match there we can
567  * provide an authoritative answer without the dht.
568  * If we don't we have to start querying the dht.
569  *
570  * FIXME now it is possible that we have a foreign zone (or even the result)
571  * cached in our namestore. Look up as well? We need a list of cached zones
572  * then.
573  *
574  * @param rh the request handle of the DNS request from a client
575  * @param name the name to look up
576  * @param id the id of the dns request (for the reply)
577  * @param type the record type to look for
578  */
579 void
580 start_resolution(struct GNUNET_DNS_RequestHandle *rh,
581                  char* name, uint16_t id, uint16_t type)
582 {
583   struct GNUNET_GNS_PendingQuery *query;
584   
585   //FIXME remove .gnunet here from name
586   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "This is .gnunet (%s)!\n", name);
587   query = GNUNET_malloc(sizeof (struct GNUNET_GNS_PendingQuery));
588   query->id = id;
589   query->original_name = name; //Full name of original query
590   query->name = name; // FIXME without tld
591   query->type = type;
592   query->request_handle = rh;
593
594   //Start resolution in our zone
595   resolve_name(query, zone_hash);
596 }
597
598 /**
599  * The DNS request handler
600  * Called for every incoming DNS request.
601  *
602  * @param cls closure
603  * @param rh request handle to user for reply
604  * @param request_length number of bytes in request
605  * @param request udp payload of the DNS request
606  */
607 void
608 handle_dns_request(void *cls,
609                    struct GNUNET_DNS_RequestHandle *rh,
610                    size_t request_length,
611                    const char *request)
612 {
613   struct GNUNET_DNSPARSER_Packet *p;
614   int i;
615   char *tldoffset;
616
617   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Hijacked a DNS request...processing\n");
618   p = GNUNET_DNSPARSER_parse (request, request_length);
619   
620   if (NULL == p)
621   {
622     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
623                 "Received malformed DNS packet, leaving it untouched\n");
624     GNUNET_DNS_request_forward (rh);
625     return;
626   }
627   
628   /**
629    * Check tld and decide if we or
630    * legacy dns is responsible
631    *
632    * FIXME now in theory there could be more than 1 query in the request
633    * but if this is case we get into trouble:
634    * either we query the GNS or the DNS. We cannot do both!
635    * So I suggest to either only allow a single query per request or
636    * only allow GNS or DNS requests.
637    * The way it is implemented here now is buggy and will lead to erratic
638    * behaviour (if multiple queries are present).
639    */
640   for (i=0;i<p->num_queries;i++)
641   {
642     tldoffset = p->queries[i].name + strlen(p->queries[i].name);
643
644     while ((*tldoffset) != '.')
645       tldoffset--;
646     
647     /**
648      * FIXME Move our tld/root to config file
649      */
650     if (0 == strcmp(tldoffset, ".gnunet"))
651     {
652       start_resolution(rh, p->queries[i].name, p->id, p->queries[i].type);
653     }
654     else
655     {
656       /**
657        * This request does not concern us. Forward to real DNS.
658        */
659       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
660                  "Request for %s is forwarded to DNS\n", p->queries[i].name);
661       GNUNET_DNS_request_forward (rh);
662     }
663   }
664 }
665
666 /**
667  * test function that stores some data in the namestore
668  */
669 void
670 put_some_records(void)
671 {
672   /* put a few records into namestore */
673   char* ipA = "1.2.3.4";
674   char* ipB = "5.6.7.8";
675   struct in_addr *alice = GNUNET_malloc(sizeof(struct in_addr));
676   struct in_addr *bob = GNUNET_malloc(sizeof(struct in_addr));
677   GNUNET_assert(1 == inet_pton (AF_INET, ipA, alice));
678   GNUNET_assert(1 == inet_pton (AF_INET, ipB, bob));
679   GNUNET_NAMESTORE_record_put (namestore_handle,
680                                zone_hash,
681                                "alice",
682                                GNUNET_GNS_RECORD_TYPE_A,
683                                GNUNET_TIME_absolute_get_forever(),
684                                GNUNET_NAMESTORE_RF_AUTHORITY,
685                                NULL, //sig loc
686                                sizeof(struct in_addr),
687                                alice,
688                                NULL,
689                                NULL);
690   GNUNET_NAMESTORE_record_put (namestore_handle,
691                                zone_hash,
692                                "bob",
693                                GNUNET_GNS_RECORD_TYPE_A,
694                                GNUNET_TIME_absolute_get_forever(),
695                                GNUNET_NAMESTORE_RF_AUTHORITY,
696                                NULL, //sig loc
697                                sizeof(struct in_addr),
698                                bob,
699                                NULL,
700                                NULL);
701 }
702
703 //Prototype... needed in put function
704 static void
705 update_zone_dht(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
706
707 /**
708  * Function used to put all records successively into the DHT.
709  * FIXME also serializes records. maybe do this somewhere else...
710  * FIXME don't store private records (maybe zone transfer does this)
711  *
712  * @param cls the closure (NULL)
713  * @param zone our root zone hash
714  * @param name the name of the record
715  * @param record_type the type of the record
716  * @param expiration lifetime of the record
717  * @param flags flags of the record
718  * @param sig_loc location of record in signature tree
719  * @param size size of the record
720  * @param record_data the record data
721  */
722 void
723 put_gns_record(void *cls, const GNUNET_HashCode *zone, const char *name,
724                uint32_t record_type, struct GNUNET_TIME_Absolute expiration,
725                enum GNUNET_NAMESTORE_RecordFlags flags,
726                const struct GNUNET_NAMESTORE_SignatureLocation *sig_loc,
727                size_t size, const void *record_data)
728 {
729   struct GNUNET_TIME_Relative timeout;
730
731   char* data;
732   char* data_ptr;
733   struct GNUNET_TIME_AbsoluteNBO exp_nbo;
734   exp_nbo = GNUNET_TIME_absolute_hton (expiration);
735   uint32_t namelen = htonl(strlen(name));
736   uint16_t flags_nbo = htons(flags);
737   uint64_t offset = GNUNET_htonll(sig_loc->offset);
738   uint32_t depth = htonl(sig_loc->depth);
739   uint32_t revision = htonl(sig_loc->revision);
740   GNUNET_HashCode name_hash;
741   GNUNET_HashCode xor_hash;
742
743   /**
744    * I guess this can be done prettier
745    * FIXME extract into function, maybe even into different file
746    */
747   size_t record_len = sizeof(size_t) + sizeof(uint32_t) +
748     sizeof(uint16_t) +
749     sizeof(struct GNUNET_NAMESTORE_SignatureLocation) +
750     sizeof(uint32_t) + strlen(name) + size;
751   
752   record_type = htonl(record_type);
753
754   data = GNUNET_malloc(record_len);
755   
756   /* -_- */
757   data_ptr = data;
758   memcpy(data_ptr, &namelen, sizeof(size_t));
759   data_ptr += sizeof(size_t);
760
761   memcpy(data_ptr, name, namelen);
762   data_ptr += namelen;
763   
764   memcpy(data_ptr, &record_type, sizeof(uint32_t));
765   data_ptr += sizeof(uint32_t);
766
767   memcpy(data_ptr, &exp_nbo, sizeof(struct GNUNET_TIME_AbsoluteNBO));
768   data_ptr += sizeof(struct GNUNET_TIME_AbsoluteNBO);
769
770   memcpy(data_ptr, &flags_nbo, sizeof(uint16_t));
771   data_ptr += sizeof(uint16_t);
772
773   memcpy(data_ptr, &offset, sizeof(uint64_t));
774   data_ptr += sizeof(uint64_t);
775
776   memcpy(data_ptr, &depth, sizeof(uint32_t));
777   data_ptr += sizeof(uint32_t);
778   
779   memcpy(data_ptr, &revision, sizeof(uint32_t));
780   data_ptr += sizeof(uint32_t);
781
782   memcpy(data_ptr, &size, sizeof(uint32_t));
783   data_ptr += sizeof(uint32_t);
784
785   /**
786    * FIXME note that this only works with raw data in nbo
787    * write helper function that converts properly and returns buffer
788    */
789   memcpy(data_ptr, record_data, size);
790   data_ptr += size;
791   /*Doing this made me sad...*/
792
793   /**
794    * FIXME magic number 20 move to config file
795    */
796   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20);
797
798   GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
799   GNUNET_CRYPTO_hash_xor(zone_hash, &name_hash, &xor_hash);
800   GNUNET_DHT_put (dht_handle, &xor_hash,
801                   5, //replication level
802                   GNUNET_DHT_RO_NONE,
803                   GNUNET_BLOCK_TYPE_TEST, //FIXME todo block plugin
804                   (data_ptr-data),
805                   data,
806                   expiration, //FIXME from record makes sense? is absolute?
807                   timeout,
808                   NULL, //FIXME continuation needed? success check? yes ofc
809                   NULL); //cls for cont
810
811   /**
812    * Reschedule periodic put
813    */
814   GNUNET_SCHEDULER_add_delayed (dht_update_interval,
815                                 &update_zone_dht,
816                                 NULL);
817
818 }
819
820 /**
821  * Periodically iterate over our zone and store everything in dht
822  *
823  * @param cls NULL
824  * @param tc task context
825  */
826 static void
827 update_zone_dht(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
828 {
829   GNUNET_NAMESTORE_zone_transfer (namestore_handle, zone_hash,
830                                   &put_gns_record,
831                                   NULL);
832 }
833
834 /**
835  * Process GNS requests.
836  *
837  * @param cls closure
838  * @param server the initialized server
839  * @param c configuration to use
840  */
841 static void
842 run (void *cls, struct GNUNET_SERVER_Handle *server,
843      const struct GNUNET_CONFIGURATION_Handle *c)
844 {
845   
846   zone_key = GNUNET_CRYPTO_rsa_key_create ();
847   GNUNET_CRYPTO_hash(zone_key, GNUNET_CRYPTO_RSA_KEY_LENGTH,//FIXME is this ok?
848                      zone_hash);
849
850   nc = GNUNET_SERVER_notification_context_create (server, 1);
851
852   /* FIXME - do some config parsing 
853    *       - Maybe only hijack dns if option is set (HIJACK_DNS=1)
854    */
855
856   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
857                                 NULL);
858   /**
859    * Do gnunet dns init here
860    */
861   dns_handle = GNUNET_DNS_connect(c,
862                                   GNUNET_DNS_FLAG_PRE_RESOLUTION,
863                                   &handle_dns_request, /* rh */
864                                   NULL); /* Closure */
865
866   if (NULL == dns_handle)
867   {
868     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
869                "Failed to connect to the dnsservice!\n");
870   }
871
872   /**
873    * handle to our local namestore
874    */
875   namestore_handle = GNUNET_NAMESTORE_connect(c);
876
877   if (NULL == namestore_handle)
878   {
879     //FIXME do error handling;
880     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
881                "Failed to connect to the namestore!\n");
882   }
883
884   /**
885    * handle to the dht
886    */
887   dht_handle = GNUNET_DHT_connect(c, 1); //FIXME get ht_len from cfg
888
889   if (NULL == dht_handle)
890   {
891     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
892   }
893
894   put_some_records(); //FIXME for testing
895   
896   /**
897    * Schedule periodic put
898    * for our records
899    */
900   dht_update_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
901                                                       60); //FIXME from cfg
902   GNUNET_SCHEDULER_add_delayed (dht_update_interval,
903                                 &update_zone_dht,
904                                 NULL);
905
906 }
907
908
909 /**
910  * The main function for the GNS service.
911  *
912  * @param argc number of arguments from the command line
913  * @param argv command line arguments
914  * @return 0 ok, 1 on error
915  */
916 int
917 main (int argc, char *const *argv)
918 {
919   int ret;
920
921   ret =
922       (GNUNET_OK ==
923        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
924                            NULL)) ? 0 : 1;
925   return ret;
926 }
927
928 /* end of gnunet-service-gns.c */