3b5119a8fd80f8ff256408764f66beabefa70fa4
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file namecache/gnunet-service-namecache.c
21  * @brief namecache for the GNUnet naming system
22  * @author Matthias Wachs
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_dnsparser_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_namecache_service.h"
30 #include "gnunet_namecache_plugin.h"
31 #include "gnunet_signatures.h"
32 #include "namecache.h"
33
34 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
35
36
37 /**
38  * A namecache client
39  */
40 struct NamecacheClient
41 {
42
43   /**
44    * The client
45    */
46   struct GNUNET_SERVICE_Client *client;
47
48   /**
49    * The message queue to talk to @e client.
50    */
51   struct GNUNET_MQ_Handle *mq;
52
53 };
54
55
56 /**
57  * Configuration handle.
58  */
59 static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg;
60
61 /**
62  * Handle to the statistics service
63  */
64 static struct GNUNET_STATISTICS_Handle *statistics;
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 /**
78  * Task run during shutdown.
79  *
80  * @param cls unused
81  */
82 static void
83 cleanup_task (void *cls)
84 {
85   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86               "Stopping namecache service\n");
87   GNUNET_break (NULL ==
88                 GNUNET_PLUGIN_unload (db_lib_name,
89                                       GSN_database));
90   GNUNET_free (db_lib_name);
91   db_lib_name = NULL;
92   if (NULL != statistics)
93   {
94     GNUNET_STATISTICS_destroy (statistics,
95                                GNUNET_NO);
96     statistics = NULL;
97   }
98 }
99
100
101 /**
102  * Called whenever a client is disconnected.
103  * Frees our resources associated with that client.
104  *
105  * @param cls closure
106  * @param client identification of the client
107  * @param app_ctx the `struct NamecacheClient` for this @a client
108  */
109 static void
110 client_disconnect_cb (void *cls,
111                       struct GNUNET_SERVICE_Client *client,
112                       void *app_ctx)
113 {
114   struct NamecacheClient *nc = app_ctx;
115
116   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
117               "Client %p disconnected\n",
118               client);
119   GNUNET_free (nc);
120 }
121
122
123 /**
124  * Add a client to our list of active clients.
125  *
126  * @param cls NULL
127  * @param client client to add
128  * @param mq queue to talk to @a client
129  * @return internal namecache client structure for this client
130  */
131 static void *
132 client_connect_cb (void *cls,
133                    struct GNUNET_SERVICE_Client *client,
134                    struct GNUNET_MQ_Handle *mq)
135 {
136   struct NamecacheClient *nc;
137
138   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
139               "Client %p connected\n",
140               client);
141   nc = GNUNET_new (struct NamecacheClient);
142   nc->client = client;
143   nc->mq = mq;
144   return nc;
145 }
146
147
148 /**
149  * Context for name lookups passed from #handle_lookup_block to
150  * #handle_lookup_block_it as closure
151  */
152 struct LookupBlockContext
153 {
154   /**
155    * The client to send the response to
156    */
157   struct NamecacheClient *nc;
158
159   /**
160    * Operation id for the name lookup
161    */
162   uint32_t request_id;
163   
164   /**
165    * Lookup status
166    */
167   int status;
168 };
169
170
171 /**
172  * A #GNUNET_NAMECACHE_BlockCallback for name lookups in #handle_lookup_block
173  *
174  * @param cls a `struct LookupNameContext *` with information about the request
175  * @param block the block
176  */
177 static void
178 handle_lookup_block_it (void *cls,
179                         const struct GNUNET_GNSRECORD_Block *block)
180 {
181   struct LookupBlockContext *lnc = cls;
182   struct GNUNET_MQ_Envelope *env;
183   struct LookupBlockResponseMessage *r;
184   size_t esize;
185   size_t bsize;
186
187   bsize = ntohl (block->purpose.size);
188   if (bsize < 
189       (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + sizeof (struct GNUNET_TIME_AbsoluteNBO)))
190   {
191     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
192                 "Malformed block.");
193     lnc->status = GNUNET_SYSERR;
194     return;
195   }
196   esize = ntohl (block->purpose.size)
197     - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)
198     - sizeof (struct GNUNET_TIME_AbsoluteNBO);
199   env = GNUNET_MQ_msg_extra (r,
200                              esize,
201                              GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
202   r->gns_header.r_id = htonl (lnc->request_id);
203   r->expire = block->expiration_time;
204   r->signature = block->signature;
205   r->derived_key = block->derived_key;
206   GNUNET_memcpy (&r[1],
207                  &block[1],
208                  esize);
209   GNUNET_STATISTICS_update (statistics,
210                             "blocks found in cache",
211                             1,
212                             GNUNET_NO);
213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214               "Sending NAMECACHE_LOOKUP_BLOCK_RESPONSE message with expiration time %s\n",
215               GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (r->expire)));
216   GNUNET_MQ_send (lnc->nc->mq,
217                   env);
218 }
219
220
221 /**
222  * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK message
223  *
224  * @param cls a `struct NamecacheClient *`
225  * @param the inbound message
226  */
227 static void
228 handle_lookup_block (void *cls,
229                      const struct LookupBlockMessage *ln_msg)
230 {
231   struct NamecacheClient *nc = cls;
232   struct GNUNET_MQ_Envelope *env;
233   struct LookupBlockContext lnc;
234   struct LookupBlockResponseMessage *zir_end;
235   int ret;
236
237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238               "Received NAMECACHE_LOOKUP_BLOCK message\n");
239   GNUNET_STATISTICS_update (statistics,
240                             "blocks looked up",
241                             1,
242                             GNUNET_NO);
243   lnc.request_id = ntohl (ln_msg->gns_header.r_id);
244   lnc.nc = nc;
245   lnc.status = GNUNET_OK;
246   if (GNUNET_SYSERR ==
247       (ret = GSN_database->lookup_block (GSN_database->cls,
248                                          &ln_msg->query,
249                                          &handle_lookup_block_it,
250                                          &lnc)))
251   {
252     /* internal error (in database plugin); might be best to just hang up on
253        plugin rather than to signal that there are 'no' results, which
254        might also be false... */
255     GNUNET_break (0);
256     GNUNET_SERVICE_client_drop (nc->client);
257     return;
258   }
259   if ((0 == ret) || (GNUNET_SYSERR == lnc.status))
260   {
261     /* no records match at all, generate empty response */
262     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263                 "Sending empty NAMECACHE_LOOKUP_BLOCK_RESPONSE message\n");
264     env = GNUNET_MQ_msg (zir_end,
265                          GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
266     zir_end->gns_header.r_id = ln_msg->gns_header.r_id;
267     GNUNET_MQ_send (nc->mq,
268                     env);
269   }
270   GNUNET_SERVICE_client_continue (nc->client);
271 }
272
273
274 /**
275  * Check a #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE message
276  *
277  * @param cls our `struct NamecacheClient`
278  * @param rp_msg message to process
279  * @return #GNUNET_OK (always fine)
280  */
281 static int
282 check_block_cache (void *cls,
283                    const struct BlockCacheMessage *rp_msg)
284 {
285   return GNUNET_OK;
286 }
287
288
289 /**
290  * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE message
291  *
292  * @param cls our `struct NamecacheClient`
293  * @param rp_msg message to process
294  */
295 static void
296 handle_block_cache (void *cls,
297                     const struct BlockCacheMessage *rp_msg)
298 {
299   struct NamecacheClient *nc = cls;
300   struct GNUNET_MQ_Envelope *env;
301   struct BlockCacheResponseMessage *rpr_msg;
302   struct GNUNET_GNSRECORD_Block *block;
303   size_t esize;
304   int res;
305
306   GNUNET_STATISTICS_update (statistics,
307                             "blocks cached",
308                             1,
309                             GNUNET_NO);
310   esize = ntohs (rp_msg->gns_header.header.size) - sizeof (struct BlockCacheMessage);
311   block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block) + esize);
312   block->signature = rp_msg->signature;
313   block->derived_key = rp_msg->derived_key;
314   block->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
315                                sizeof (struct GNUNET_TIME_AbsoluteNBO) +
316                                esize);
317   block->expiration_time = rp_msg->expire;
318   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319               "Received NAMECACHE_BLOCK_CACHE message with expiration time %s\n",
320               GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (block->expiration_time)));
321   GNUNET_memcpy (&block[1],
322                  &rp_msg[1],
323                  esize);
324   res = GSN_database->cache_block (GSN_database->cls,
325                                    block);
326   GNUNET_free (block);
327   env = GNUNET_MQ_msg (rpr_msg,
328                        GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE);
329   rpr_msg->gns_header.r_id = rp_msg->gns_header.r_id;
330   rpr_msg->op_result = htonl (res);
331   GNUNET_MQ_send (nc->mq,
332                   env);
333   GNUNET_SERVICE_client_continue (nc->client);
334 }
335
336
337 /**
338  * Process namecache requests.
339  *
340  * @param cls closure
341  * @param cfg configuration to use
342  * @param service the initialized service
343  */
344 static void
345 run (void *cls,
346      const struct GNUNET_CONFIGURATION_Handle *cfg,
347      struct GNUNET_SERVICE_Handle *service)
348 {
349   char *database;
350
351   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352               "Starting namecache service\n");
353   GSN_cfg = cfg;
354
355   /* Loading database plugin */
356   if (GNUNET_OK !=
357       GNUNET_CONFIGURATION_get_value_string (cfg,
358                                              "namecache",
359                                              "database",
360                                              &database))
361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                 "No database backend configured\n");
363
364   GNUNET_asprintf (&db_lib_name,
365                    "libgnunet_plugin_namecache_%s",
366                    database);
367   GSN_database = GNUNET_PLUGIN_load (db_lib_name,
368                                      (void *) GSN_cfg);
369   GNUNET_free (database);
370   if (NULL == GSN_database)
371   {
372     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
373                 "Could not load database backend `%s'\n",
374                 db_lib_name);
375     GNUNET_SCHEDULER_add_now (&cleanup_task,
376                               NULL);
377     return;
378   }
379   statistics = GNUNET_STATISTICS_create ("namecache",
380                                          cfg);
381
382   /* Configuring server handles */
383   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
384                                  NULL);
385 }
386
387
388 /**
389  * Define "main" method using service macro.
390  */
391 GNUNET_SERVICE_MAIN
392 ("namecache",
393  GNUNET_SERVICE_OPTION_NONE,
394  &run,
395  &client_connect_cb,
396  &client_disconnect_cb,
397  NULL,
398  GNUNET_MQ_hd_fixed_size (lookup_block,
399                           GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK,
400                           struct LookupBlockMessage,
401                           NULL),
402  GNUNET_MQ_hd_var_size (block_cache,
403                         GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE,
404                         struct BlockCacheMessage,
405                         NULL),
406  GNUNET_MQ_handler_end ());
407
408
409 /* end of gnunet-service-namecache.c */