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