-GNS service api change, replaced complicated buggy code
[oweals/gnunet.git] / src / gns / gns_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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  * @file gns/gns_api.c
24  * @brief library to access the GNS service
25  * @author Martin Schanzenbach
26  */
27
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_arm_service.h"
32 #include "gnunet_hello_lib.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_dht_service.h"
35 #include "gns.h"
36 #include "gnunet_gns_service.h"
37
38 #define DEBUG_GNS_API GNUNET_EXTRA_LOGGING
39
40 #define LOG(kind,...) GNUNET_log_from (kind, "gns-api",__VA_ARGS__)
41
42 /* TODO into gnunet_protocols */
43 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP 23
44 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT 24
45 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN 25
46 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT 26
47
48 /**
49  * A QueueEntry.
50  */
51 struct GNUNET_GNS_QueueEntry
52 {
53   /**
54    * DLL
55    */
56   struct GNUNET_GNS_QueueEntry *next;
57   
58   /**
59    * DLL
60    */
61   struct GNUNET_GNS_QueueEntry *prev;
62
63   /* request id */
64   uint32_t r_id;
65   
66   /* handle to gns */
67   struct GNUNET_GNS_Handle *gns_handle;
68   
69   /* processor to call on shorten result */
70   GNUNET_GNS_ShortenResultProcessor proc;
71   
72   /* processor closure */
73   void *proc_cls;
74   
75 };
76
77
78 /**
79  * Entry in our list of messages to be (re-)transmitted.
80  */
81 struct PendingMessage
82 {
83   /**
84    * This is a doubly-linked list.
85    */
86   struct PendingMessage *prev;
87
88   /**
89    * This is a doubly-linked list.
90    */
91   struct PendingMessage *next;
92
93   /**
94    * Size of the message.
95    */
96   size_t size;
97
98 };
99
100
101 /**
102  * Connection to the GNS service.
103  */
104 struct GNUNET_GNS_Handle
105 {
106
107   /**
108    * Configuration to use.
109    */
110   const struct GNUNET_CONFIGURATION_Handle *cfg;
111
112   /**
113    * Socket (if available).
114    */
115   struct GNUNET_CLIENT_Connection *client;
116
117   /**
118    * Currently pending transmission request (or NULL).
119    */
120   struct GNUNET_CLIENT_TransmitHandle *th;
121   
122   uint32_t r_id;
123   
124   /**
125    * Head of linked list of shorten messages we would like to transmit.
126    */
127   struct PendingMessage *pending_head;
128
129   /**
130    * Tail of linked list of shorten messages we would like to transmit.
131    */
132   struct PendingMessage *pending_tail;
133   
134   /**
135    * Head of linked list of shorten messages we would like to transmit.
136    */
137   struct GNUNET_GNS_QueueEntry *shorten_head;
138
139   /**
140    * Tail of linked list of shorten messages we would like to transmit.
141    */
142   struct GNUNET_GNS_QueueEntry *shorten_tail;
143   
144   /**
145    * Head of linked list of lookup messages we would like to transmit.
146    */
147   struct GNUNET_GNS_QueueEntry *lookup_head;
148
149   /**
150    * Tail of linked list of lookup messages we would like to transmit.
151    */
152   struct GNUNET_GNS_QueueEntry *lookup_tail;
153   
154   /**
155    * Reconnect task
156    */
157   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
158
159   /**
160    * Did we start our receive loop yet?
161    */
162   int in_receive;
163
164   /**
165    * Reconnect necessary
166    */
167   int reconnect;
168 };
169
170 /**
171  * Try to send messages from list of messages to send
172  * @param handle GNS_Handle
173  */
174 static void
175 process_pending_messages (struct GNUNET_GNS_Handle *handle);
176
177
178 /**
179  * Reconnect to GNS service.
180  *
181  * @param h the handle to the namestore service
182  */
183 static void
184 reconnect (struct GNUNET_GNS_Handle *h)
185 {
186   GNUNET_assert (NULL == h->client);
187   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
188              "Trying to connect to GNS...\n");
189   h->client = GNUNET_CLIENT_connect ("gns", h->cfg);
190   GNUNET_assert (NULL != h->client);
191 }
192
193 /**
194  * Reconnect to GNS
195  *
196  * @param cls the handle
197  * @param tc task context
198  */
199 static void
200 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
201 {
202   struct GNUNET_GNS_Handle *h = cls;
203
204   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
205   reconnect (h);
206 }
207
208
209 /**
210  * Disconnect from service and then reconnect.
211  *
212  * @param h our handle
213  */
214 static void
215 force_reconnect (struct GNUNET_GNS_Handle *h)
216 {
217   h->reconnect = GNUNET_NO;
218   GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
219   h->client = NULL;
220   h->reconnect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
221                                                     &reconnect_task,
222                                                     h);
223 }
224
225 /**
226  * Transmit the next pending message, called by notify_transmit_ready
227  */
228 static size_t
229 transmit_pending (void *cls, size_t size, void *buf);
230
231 /**
232  * Handler for messages received from the GNS service
233  *
234  * @param cls the 'struct GNUNET_GNS_Handle'
235  * @param msg the incoming message
236  */
237 static void
238 process_message (void *cls, const struct GNUNET_MessageHeader *msg);
239
240 /**
241  * Try to send messages from list of messages to send
242  */
243 static void
244 process_pending_messages (struct GNUNET_GNS_Handle *handle)
245 {
246   struct PendingMessage *p;
247
248   if (handle->client == NULL)
249   {
250     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
251          "process_pending_messages called, but client is null\n");
252     return;
253   }
254   
255   if (handle->th != NULL)
256     return;
257   
258   if (NULL == (p = handle->pending_head))
259     return;
260   
261   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
262              "Trying to transmit %d bytes...\n", p->size);
263
264   handle->th =
265     GNUNET_CLIENT_notify_transmit_ready (handle->client,
266                                          p->size,
267                                          GNUNET_TIME_UNIT_FOREVER_REL,
268                                          GNUNET_NO, &transmit_pending,
269                                          handle);
270   if (NULL != handle->th)
271     return;
272
273   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274               "notify_transmit_ready returned NULL!\n");
275 }
276
277
278 /**
279  * Transmit the next pending message, called by notify_transmit_ready
280  */
281 static size_t
282 transmit_pending (void *cls, size_t size, void *buf)
283 {
284   struct GNUNET_GNS_Handle *handle = cls;
285   struct PendingMessage *p;
286   size_t tsize;
287   char *cbuf;
288
289   handle->th = NULL;
290   
291   if ((size == 0) || (buf == NULL))
292   {
293     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
294          "Transmission to GNS service failed!\n");
295     force_reconnect(handle);
296     return 0;
297   }
298   
299   tsize = 0;
300   cbuf = buf;
301
302   if (NULL == (p = handle->pending_head))
303     return 0;
304
305   while ((NULL != (p = handle->pending_head)) && (p->size <= size))
306   {
307     memcpy (&cbuf[tsize], &p[1], p->size);
308     tsize += p->size;
309     size -= p->size;
310     GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail, p);
311     if (GNUNET_YES != handle->in_receive)
312     {
313       GNUNET_CLIENT_receive (handle->client, &process_message, handle,
314                              GNUNET_TIME_UNIT_FOREVER_REL);
315       handle->in_receive = GNUNET_YES;
316     }
317     GNUNET_free(p);
318   }
319
320   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321               "Sending %d bytes\n", tsize);
322
323   process_pending_messages(handle);
324   return tsize;
325 }
326
327 /**
328  * Process a given reply that might match the given
329  * request.
330  *
331  * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
332  * @param key query of the request
333  * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
334  */
335 static void
336 process_shorten_reply (struct GNUNET_GNS_QueueEntry *qe,
337                        const struct GNUNET_GNS_ClientShortenResultMessage *msg)
338 {
339   struct GNUNET_GNS_Handle *h = qe->gns_handle;
340   const char *short_name;
341
342   GNUNET_CONTAINER_DLL_remove(h->shorten_head, h->shorten_tail, qe);
343
344   short_name = (char*)(&msg[1]);
345
346   if (ntohs (((struct GNUNET_MessageHeader*)msg)->size) <
347       sizeof (struct GNUNET_GNS_ClientShortenResultMessage))
348   {
349     GNUNET_break (0);
350     force_reconnect (h);
351     return;
352   }
353   
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355               "Received shortened reply `%s' from GNS service\n",
356               short_name);
357   
358   qe->proc(qe->proc_cls, short_name);
359
360 }
361
362
363 /**
364  * Process a given reply to the lookup request
365  *
366  * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
367  * @param key query of the request
368  * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
369  * @return GNUNET_YES to continue to iterate over all results,
370  *         GNUNET_NO if the reply is malformed
371  */
372 static void
373 process_lookup_reply (struct GNUNET_GNS_QueueEntry *qe,
374                       const struct GNUNET_GNS_ClientLookupResultMessage *msg)
375 {
376 }
377
378 /**
379  * Handler for messages received from the GNS service
380  *
381  * @param cls the 'struct GNUNET_GNS_Handle'
382  * @param msg the incoming message
383  */
384 static void
385 process_message (void *cls, const struct GNUNET_MessageHeader *msg)
386 {
387   struct GNUNET_GNS_Handle *handle = cls;
388   struct GNUNET_GNS_QueueEntry *qe;
389   const struct GNUNET_GNS_ClientLookupResultMessage *lookup_msg;
390   const struct GNUNET_GNS_ClientShortenResultMessage *shorten_msg;
391   uint16_t size;
392   uint16_t type;
393   uint32_t r_id;
394   
395   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
396               "Got message\n");
397   if (msg == NULL)
398   {
399     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400          "Error receiving data from GNS service, reconnecting\n");
401     force_reconnect (handle);
402     return;
403   }
404
405   size = ntohs (msg->size);
406   type = ntohs (msg->type);
407
408   if (type == GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT)
409   {
410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411                 "Got lookup msg\n");
412     lookup_msg = (const struct GNUNET_GNS_ClientLookupResultMessage *) msg;
413     r_id = ntohl (lookup_msg->id);
414     
415     if (r_id > handle->r_id)
416     {
417       /** no request found */
418       GNUNET_break_op (0);
419       GNUNET_CLIENT_receive (handle->client, &process_message, handle,
420                              GNUNET_TIME_UNIT_FOREVER_REL);
421     }
422
423     for (qe = handle->lookup_head; qe != NULL; qe = qe->next)
424     {
425       if (qe->r_id == r_id)
426         break;
427     }
428     if (qe)
429       process_lookup_reply(qe, lookup_msg);
430
431
432   }
433   else if (type == GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT)
434   {
435     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436                 "Got shorten msg\n");
437     shorten_msg = (struct GNUNET_GNS_ClientShortenResultMessage *) msg;
438     
439     r_id = ntohl (shorten_msg->id);
440     
441     if (r_id > handle->r_id)
442     {
443       /** no request found */
444       GNUNET_break_op (0);
445       GNUNET_CLIENT_receive (handle->client, &process_message, handle,
446                              GNUNET_TIME_UNIT_FOREVER_REL);
447     }
448
449     for (qe = handle->shorten_head; qe != NULL; qe = qe->next)
450     {
451       if (qe->r_id == r_id)
452         break;
453     }
454     if (qe)
455       process_shorten_reply(qe, shorten_msg);
456   }
457
458   GNUNET_CLIENT_receive (handle->client, &process_message, handle,
459                          GNUNET_TIME_UNIT_FOREVER_REL);
460
461   if (GNUNET_YES == handle->reconnect)
462     force_reconnect (handle);
463   
464 }
465
466
467 /**
468  * Initialize the connection with the GNS service.
469  *
470  * @param cfg configuration to use
471  * @param ht_len size of the internal hash table to use for parallel requests
472  * @return handle to the GNS service, or NULL on error
473  */
474 struct GNUNET_GNS_Handle *
475 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
476 {
477   struct GNUNET_GNS_Handle *handle;
478
479   handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
480   handle->cfg = cfg;
481   reconnect (handle);
482   //handle->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect_task, handle);
483   handle->r_id = 0;
484   handle->in_receive = GNUNET_NO;
485   return handle;
486 }
487
488
489 /**
490  * Shutdown connection with the GNS service.
491  *
492  * @param handle handle of the GNS connection to stop
493  */
494 void
495 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
496 {
497   /* disco from GNS */
498 }
499
500 /*
501  * Helper function to generate request ids
502  * 
503  * @param h handle
504  * @return a new id
505  */
506 static uint32_t
507 get_request_id (struct GNUNET_GNS_Handle *h)
508 {
509   uint32_t r_id = h->r_id;
510   h->r_id++;
511   return r_id;
512 }
513
514 /**
515  * Perform an asynchronous Lookup operation on the GNS.
516  *
517  * @param handle handle to the GNS service
518  * @param name the name to look up
519  * @param iter function to call on each result
520  * @param iter_cls closure for iter
521  * @return handle to stop the async get
522  */
523 struct GNUNET_GNS_QueueEntry *
524 GNUNET_GNS_lookup (struct GNUNET_GNS_Handle *handle,
525                          const char * name,
526                          enum GNUNET_GNS_RecordType type,
527                          GNUNET_GNS_LookupIterator iter,
528                          void *iter_cls)
529 {
530   return NULL;
531 }
532
533
534 /**
535  * Perform a name shortening operation on the GNS.
536  *
537  * @param handle handle to the GNS service
538  * @param name the name to look up
539  * @param proc function to call on result
540  * @param proc_cls closure for processor
541  * @return handle to the operation
542  */
543 struct GNUNET_GNS_QueueEntry *
544 GNUNET_GNS_shorten (struct GNUNET_GNS_Handle *handle,
545                     const char * name,
546                     GNUNET_GNS_ShortenResultProcessor proc,
547                     void *proc_cls)
548 {
549   /* IPC to shorten gns names, return shorten_handle */
550   struct GNUNET_GNS_ClientShortenMessage *shorten_msg;
551   struct GNUNET_GNS_QueueEntry *qe;
552   size_t msize;
553   struct PendingMessage *pending;
554
555   if (NULL == name)
556   {
557     return NULL;
558   }
559
560   msize = sizeof (struct GNUNET_GNS_ClientShortenMessage) + strlen(name) + 1;
561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to shorten %s in GNS\n", name);
562
563   qe = GNUNET_malloc(sizeof (struct GNUNET_GNS_QueueEntry));
564   qe->gns_handle = handle;
565   qe->proc = proc;
566   qe->proc_cls = proc_cls;
567   qe->r_id = get_request_id(handle);
568   GNUNET_CONTAINER_DLL_insert_tail(handle->shorten_head,
569                                    handle->shorten_tail, qe);
570
571   pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
572   memset(pending, 0, (sizeof (struct PendingMessage) + msize));
573   
574   pending->size = msize;
575
576   shorten_msg = (struct GNUNET_GNS_ClientShortenMessage *) &pending[1];
577   shorten_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_SHORTEN);
578   shorten_msg->header.size = htons (msize);
579   shorten_msg->id = htonl(qe->r_id);
580
581   memcpy(&shorten_msg[1], name, strlen(name));
582
583   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
584                                pending);
585   
586   process_pending_messages (handle);
587   return qe;
588 }
589
590
591
592 /* end of gns_api.c */