update tests to use new MQ API
[oweals/gnunet.git] / src / namecache / gnunet-service-namecache.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file namecache/gnunet-service-namecache.c
23  * @brief namecache for the GNUnet naming system
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet_namecache_service.h"
31 #include "gnunet_namecache_plugin.h"
32 #include "gnunet_signatures.h"
33 #include "namecache.h"
34
35 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
36
37
38 /**
39  * A namecache client
40  */
41 struct NamecacheClient
42 {
43   /**
44    * Next element in the DLL
45    */
46   struct NamecacheClient *next;
47
48   /**
49    * Previous element in the DLL
50    */
51   struct NamecacheClient *prev;
52
53   /**
54    * The client
55    */
56   struct GNUNET_SERVER_Client *client;
57
58 };
59
60
61 /**
62  * Configuration handle.
63  */
64 static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg;
65
66 /**
67  * Database handle
68  */
69 static struct GNUNET_NAMECACHE_PluginFunctions *GSN_database;
70
71 /**
72  * Name of the database plugin
73  */
74 static char *db_lib_name;
75
76 /**
77  * Our notification context.
78  */
79 static struct GNUNET_SERVER_NotificationContext *snc;
80
81 /**
82  * Head of the Client DLL
83  */
84 static struct NamecacheClient *client_head;
85
86 /**
87  * Tail of the Client DLL
88  */
89 static struct NamecacheClient *client_tail;
90
91 /**
92  * Notification context shared by all monitors.
93  */
94 static struct GNUNET_SERVER_NotificationContext *monitor_nc;
95
96
97
98 /**
99  * Task run during shutdown.
100  *
101  * @param cls unused
102  */
103 static void
104 cleanup_task (void *cls)
105 {
106   struct NamecacheClient *nc;
107
108   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
109               "Stopping namecache service\n");
110   if (NULL != snc)
111   {
112     GNUNET_SERVER_notification_context_destroy (snc);
113     snc = NULL;
114   }
115   while (NULL != (nc = client_head))
116   {
117     GNUNET_CONTAINER_DLL_remove (client_head, client_tail, nc);
118     GNUNET_SERVER_client_set_user_context (nc->client, NULL);
119     GNUNET_free (nc);
120   }
121   GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, GSN_database));
122   GNUNET_free (db_lib_name);
123   db_lib_name = NULL;
124   if (NULL != monitor_nc)
125   {
126     GNUNET_SERVER_notification_context_destroy (monitor_nc);
127     monitor_nc = NULL;
128   }
129 }
130
131
132 /**
133  * Called whenever a client is disconnected.
134  * Frees our resources associated with that client.
135  *
136  * @param cls closure
137  * @param client identification of the client
138  */
139 static void
140 client_disconnect_notification (void *cls,
141                                 struct GNUNET_SERVER_Client *client)
142 {
143   struct NamecacheClient *nc;
144
145   if (NULL == client)
146     return;
147   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
148               "Client %p disconnected\n",
149               client);
150   if (NULL == (nc = GNUNET_SERVER_client_get_user_context (client, struct NamecacheClient)))
151     return;
152   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, nc);
153   GNUNET_free (nc);
154 }
155
156
157 /**
158  * Add a client to our list of active clients, if it is not yet
159  * in there.
160  *
161  * @param client client to add
162  * @return internal namecache client structure for this client
163  */
164 static struct NamecacheClient *
165 client_lookup (struct GNUNET_SERVER_Client *client)
166 {
167   struct NamecacheClient *nc;
168
169   nc = GNUNET_SERVER_client_get_user_context (client, struct NamecacheClient);
170   if (NULL != nc)
171     return nc;
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Client %p connected\n",
174               client);
175   nc = GNUNET_new (struct NamecacheClient);
176   nc->client = client;
177   GNUNET_SERVER_notification_context_add (snc, client);
178   GNUNET_CONTAINER_DLL_insert (client_head, client_tail, nc);
179   GNUNET_SERVER_client_set_user_context (client, nc);
180   return nc;
181 }
182
183
184 /**
185  * Context for name lookups passed from #handle_lookup_block to
186  * #handle_lookup_block_it as closure
187  */
188 struct LookupBlockContext
189 {
190   /**
191    * The client to send the response to
192    */
193   struct NamecacheClient *nc;
194
195   /**
196    * Operation id for the name lookup
197    */
198   uint32_t request_id;
199
200 };
201
202
203 /**
204  * A #GNUNET_NAMECACHE_BlockCallback for name lookups in #handle_lookup_block
205  *
206  * @param cls a `struct LookupNameContext *` with information about the request
207  * @param block the block
208  */
209 static void
210 handle_lookup_block_it (void *cls,
211                         const struct GNUNET_GNSRECORD_Block *block)
212 {
213   struct LookupBlockContext *lnc = cls;
214   struct LookupBlockResponseMessage *r;
215   size_t esize;
216
217   esize = ntohl (block->purpose.size)
218     - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)
219     - sizeof (struct GNUNET_TIME_AbsoluteNBO);
220   r = GNUNET_malloc (sizeof (struct LookupBlockResponseMessage) + esize);
221   r->gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
222   r->gns_header.header.size = htons (sizeof (struct LookupBlockResponseMessage) + esize);
223   r->gns_header.r_id = htonl (lnc->request_id);
224   r->expire = block->expiration_time;
225   r->signature = block->signature;
226   r->derived_key = block->derived_key;
227   memcpy (&r[1], &block[1], esize);
228   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229               "Sending `%s' message with expiration time %s\n",
230               "NAMECACHE_LOOKUP_BLOCK_RESPONSE",
231               GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (r->expire)));
232   GNUNET_SERVER_notification_context_unicast (snc,
233                                               lnc->nc->client,
234                                               &r->gns_header.header,
235                                               GNUNET_NO);
236   GNUNET_free (r);
237 }
238
239
240 /**
241  * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK message
242  *
243  * @param cls unused
244  * @param client client sending the message
245  * @param message message of type 'struct LookupNameMessage'
246  */
247 static void
248 handle_lookup_block (void *cls,
249                     struct GNUNET_SERVER_Client *client,
250                     const struct GNUNET_MessageHeader *message)
251 {
252   const struct LookupBlockMessage *ln_msg;
253   struct LookupBlockContext lnc;
254   struct NamecacheClient *nc;
255   struct LookupBlockResponseMessage zir_end;
256   int ret;
257
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
259               "Received `%s' message\n",
260               "NAMECACHE_LOOKUP_BLOCK");
261   nc = client_lookup(client);
262   ln_msg = (const struct LookupBlockMessage *) message;
263   lnc.request_id = ntohl (ln_msg->gns_header.r_id);
264   lnc.nc = nc;
265   if (GNUNET_SYSERR ==
266       (ret = GSN_database->lookup_block (GSN_database->cls,
267                                          &ln_msg->query,
268                                          &handle_lookup_block_it, &lnc)))
269   {
270     /* internal error (in database plugin); might be best to just hang up on
271        plugin rather than to signal that there are 'no' results, which
272        might also be false... */
273     GNUNET_break (0);
274     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
275     return;
276   }
277   if (0 == ret)
278   {
279     /* no records match at all, generate empty response */
280     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281                 "Sending empty `%s' message\n",
282                 "NAMECACHE_LOOKUP_BLOCK_RESPONSE");
283     memset (&zir_end, 0, sizeof (zir_end));
284     zir_end.gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
285     zir_end.gns_header.header.size = htons (sizeof (struct LookupBlockResponseMessage));
286     zir_end.gns_header.r_id = ln_msg->gns_header.r_id;
287     GNUNET_SERVER_notification_context_unicast (snc,
288                                                 client,
289                                                 &zir_end.gns_header.header,
290                                                 GNUNET_NO);
291
292   }
293   GNUNET_SERVER_receive_done (client, GNUNET_OK);
294 }
295
296
297 /**
298  * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE message
299  *
300  * @param cls unused
301  * @param client client sending the message
302  * @param message message of type 'struct BlockCacheMessage'
303  */
304 static void
305 handle_block_cache (void *cls,
306                      struct GNUNET_SERVER_Client *client,
307                      const struct GNUNET_MessageHeader *message)
308 {
309   struct NamecacheClient *nc;
310   const struct BlockCacheMessage *rp_msg;
311   struct BlockCacheResponseMessage rpr_msg;
312   struct GNUNET_GNSRECORD_Block *block;
313   size_t esize;
314   int res;
315
316   nc = client_lookup (client);
317   if (ntohs (message->size) < sizeof (struct BlockCacheMessage))
318   {
319     GNUNET_break (0);
320     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
321     return;
322   }
323   rp_msg = (const struct BlockCacheMessage *) message;
324   esize = ntohs (rp_msg->gns_header.header.size) - sizeof (struct BlockCacheMessage);
325   block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block) + esize);
326   block->signature = rp_msg->signature;
327   block->derived_key = rp_msg->derived_key;
328   block->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
329                                sizeof (struct GNUNET_TIME_AbsoluteNBO) +
330                                esize);
331   block->expiration_time = rp_msg->expire;
332   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333               "Received `%s' message with expiration time %s\n",
334               "NAMECACHE_BLOCK_CACHE",
335               GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (block->expiration_time)));
336   memcpy (&block[1], &rp_msg[1], esize);
337   res = GSN_database->cache_block (GSN_database->cls,
338                                    block);
339   GNUNET_free (block);
340
341   rpr_msg.gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE);
342   rpr_msg.gns_header.header.size = htons (sizeof (struct BlockCacheResponseMessage));
343   rpr_msg.gns_header.r_id = rp_msg->gns_header.r_id;
344   rpr_msg.op_result = htonl (res);
345   GNUNET_SERVER_notification_context_unicast (snc,
346                                               nc->client,
347                                               &rpr_msg.gns_header.header,
348                                               GNUNET_NO);
349   GNUNET_SERVER_receive_done (client, GNUNET_OK);
350 }
351
352
353 /**
354  * Process namecache requests.
355  *
356  * @param cls closure
357  * @param server the initialized server
358  * @param cfg configuration to use
359  */
360 static void
361 run (void *cls, struct GNUNET_SERVER_Handle *server,
362      const struct GNUNET_CONFIGURATION_Handle *cfg)
363 {
364   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
365     {&handle_lookup_block, NULL,
366      GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK, sizeof (struct LookupBlockMessage)},
367     {&handle_block_cache, NULL,
368     GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE, 0},
369     {NULL, NULL, 0, 0}
370   };
371   char *database;
372
373   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting namecache service\n");
374   GSN_cfg = cfg;
375   monitor_nc = GNUNET_SERVER_notification_context_create (server, 1);
376
377   /* Loading database plugin */
378   if (GNUNET_OK !=
379       GNUNET_CONFIGURATION_get_value_string (cfg, "namecache", "database",
380                                              &database))
381     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n");
382
383   GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_namecache_%s", database);
384   GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) GSN_cfg);
385   GNUNET_free (database);
386   if (NULL == GSN_database)
387   {
388     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389                 "Could not load database backend `%s'\n",
390                 db_lib_name);
391     GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
392     return;
393   }
394
395   /* Configuring server handles */
396   GNUNET_SERVER_add_handlers (server, handlers);
397   snc = GNUNET_SERVER_notification_context_create (server, 16);
398   GNUNET_SERVER_disconnect_notify (server,
399                                    &client_disconnect_notification,
400                                    NULL);
401   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
402                                  NULL);
403 }
404
405
406 /**
407  * The main function for the template service.
408  *
409  * @param argc number of arguments from the command line
410  * @param argv command line arguments
411  * @return 0 ok, 1 on error
412  */
413 int
414 main (int argc, char *const *argv)
415 {
416   return (GNUNET_OK ==
417           GNUNET_SERVICE_run (argc, argv, "namecache",
418                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
419 }
420
421 /* end of gnunet-service-namecache.c */