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