uncrustify as demanded.
[oweals/gnunet.git] / src / namestore / plugin_namestore_sqlite.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2009-2017 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 namestore/plugin_namestore_sqlite.c
23  * @brief sqlite-based namestore backend
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_namestore_plugin.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_sq_lib.h"
32 #include "namestore.h"
33 #include <sqlite3.h>
34
35 /**
36  * After how many ms "busy" should a DB operation fail for good?  A
37  * low value makes sure that we are more responsive to requests
38  * (especially PUTs).  A high value guarantees a higher success rate
39  * (SELECTs in iterate can take several seconds despite LIMIT=1).
40  *
41  * The default value of 1s should ensure that users do not experience
42  * huge latencies while at the same time allowing operations to
43  * succeed with reasonable probability.
44  */
45 #define BUSY_TIMEOUT_MS 1000
46
47
48 /**
49  * Log an error message at log-level 'level' that indicates
50  * a failure of the command 'cmd' on file 'filename'
51  * with the message given by strerror(errno).
52  */
53 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from(level, "namestore-sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while (0)
54
55 #define LOG(kind, ...) GNUNET_log_from(kind, "namestore-sqlite", __VA_ARGS__)
56
57
58 /**
59  * Context for all functions in this plugin.
60  */
61 struct Plugin {
62   const struct GNUNET_CONFIGURATION_Handle *cfg;
63
64   /**
65    * Database filename.
66    */
67   char *fn;
68
69   /**
70    * Native SQLite database handle.
71    */
72   sqlite3 *dbh;
73
74   /**
75    * Precompiled SQL to store records.
76    */
77   sqlite3_stmt *store_records;
78
79   /**
80    * Precompiled SQL to deltete existing records.
81    */
82   sqlite3_stmt *delete_records;
83
84   /**
85    * Precompiled SQL for iterate records within a zone.
86    */
87   sqlite3_stmt *iterate_zone;
88
89   /**
90    * Precompiled SQL for iterate all records within all zones.
91    */
92   sqlite3_stmt *iterate_all_zones;
93
94   /**
95    * Precompiled SQL to for reverse lookup based on PKEY.
96    */
97   sqlite3_stmt *zone_to_name;
98
99   /**
100    * Precompiled SQL to lookup records based on label.
101    */
102   sqlite3_stmt *lookup_label;
103 };
104
105
106 /**
107  * Initialize the database connections and associated
108  * data structures (create tables and indices
109  * as needed as well).
110  *
111  * @param plugin the plugin context (state for this module)
112  * @return #GNUNET_OK on success
113  */
114 static int
115 database_setup(struct Plugin *plugin)
116 {
117   char *sqlite_filename;
118   struct GNUNET_SQ_ExecuteStatement es[] = {
119     GNUNET_SQ_make_try_execute("PRAGMA temp_store=MEMORY"),
120     GNUNET_SQ_make_try_execute("PRAGMA synchronous=NORMAL"),
121     GNUNET_SQ_make_try_execute("PRAGMA legacy_file_format=OFF"),
122     GNUNET_SQ_make_try_execute("PRAGMA auto_vacuum=INCREMENTAL"),
123     GNUNET_SQ_make_try_execute("PRAGMA encoding=\"UTF-8\""),
124     GNUNET_SQ_make_try_execute("PRAGMA locking_mode=EXCLUSIVE"),
125     GNUNET_SQ_make_try_execute("PRAGMA journal_mode=WAL"),
126     GNUNET_SQ_make_try_execute("PRAGMA page_size=4092"),
127     GNUNET_SQ_make_execute("CREATE TABLE IF NOT EXISTS ns098records ("
128                            " uid INTEGER PRIMARY KEY,"
129                            " zone_private_key BLOB NOT NULL,"
130                            " pkey BLOB,"
131                            " rvalue INT8 NOT NULL,"
132                            " record_count INT NOT NULL,"
133                            " record_data BLOB NOT NULL,"
134                            " label TEXT NOT NULL"
135                            ")"),
136     GNUNET_SQ_make_try_execute("CREATE INDEX IF NOT EXISTS ir_pkey_reverse "
137                                "ON ns098records (zone_private_key,pkey)"),
138     GNUNET_SQ_make_try_execute("CREATE INDEX IF NOT EXISTS ir_pkey_iter "
139                                "ON ns098records (zone_private_key,uid)"),
140     GNUNET_SQ_EXECUTE_STATEMENT_END
141   };
142   struct GNUNET_SQ_PrepareStatement ps[] = {
143     GNUNET_SQ_make_prepare("INSERT INTO ns098records "
144                            "(zone_private_key,pkey,rvalue,record_count,record_data,label)"
145                            " VALUES (?, ?, ?, ?, ?, ?)",
146                            &plugin->store_records),
147     GNUNET_SQ_make_prepare("DELETE FROM ns098records "
148                            "WHERE zone_private_key=? AND label=?",
149                            &plugin->delete_records),
150     GNUNET_SQ_make_prepare("SELECT uid,record_count,record_data,label"
151                            " FROM ns098records"
152                            " WHERE zone_private_key=? AND pkey=?",
153                            &plugin->zone_to_name),
154     GNUNET_SQ_make_prepare("SELECT uid,record_count,record_data,label"
155                            " FROM ns098records"
156                            " WHERE zone_private_key=? AND uid > ?"
157                            " ORDER BY uid ASC"
158                            " LIMIT ?",
159                            &plugin->iterate_zone),
160     GNUNET_SQ_make_prepare("SELECT uid,record_count,record_data,label,zone_private_key"
161                            " FROM ns098records"
162                            " WHERE uid > ?"
163                            " ORDER BY uid ASC"
164                            " LIMIT ?",
165                            &plugin->iterate_all_zones),
166     GNUNET_SQ_make_prepare("SELECT uid,record_count,record_data,label"
167                            " FROM ns098records"
168                            " WHERE zone_private_key=? AND label=?",
169                            &plugin->lookup_label),
170     GNUNET_SQ_PREPARE_END
171   };
172
173   if (GNUNET_OK !=
174       GNUNET_CONFIGURATION_get_value_filename(plugin->cfg,
175                                               "namestore-sqlite",
176                                               "FILENAME",
177                                               &sqlite_filename))
178     {
179       GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR,
180                                 "namestore-sqlite",
181                                 "FILENAME");
182       return GNUNET_SYSERR;
183     }
184   if (GNUNET_OK !=
185       GNUNET_DISK_file_test(sqlite_filename))
186     {
187       if (GNUNET_OK !=
188           GNUNET_DISK_directory_create_for_file(sqlite_filename))
189         {
190           GNUNET_break(0);
191           GNUNET_free(sqlite_filename);
192           return GNUNET_SYSERR;
193         }
194     }
195   /* sqlite_filename should be UTF-8-encoded. If it isn't, it's a bug */
196   plugin->fn = sqlite_filename;
197
198   /* Open database and precompile statements */
199   if (SQLITE_OK !=
200       sqlite3_open(plugin->fn,
201                    &plugin->dbh))
202     {
203       LOG(GNUNET_ERROR_TYPE_ERROR,
204           _("Unable to initialize SQLite: %s.\n"),
205           sqlite3_errmsg(plugin->dbh));
206       return GNUNET_SYSERR;
207     }
208   GNUNET_break(SQLITE_OK ==
209                sqlite3_busy_timeout(plugin->dbh,
210                                     BUSY_TIMEOUT_MS));
211   if (GNUNET_OK !=
212       GNUNET_SQ_exec_statements(plugin->dbh,
213                                 es))
214     {
215       GNUNET_break(0);
216       LOG(GNUNET_ERROR_TYPE_ERROR,
217           _("Failed to setup database at `%s'\n"),
218           plugin->fn);
219       return GNUNET_SYSERR;
220     }
221
222   if (GNUNET_OK !=
223       GNUNET_SQ_prepare(plugin->dbh,
224                         ps))
225     {
226       GNUNET_break(0);
227       LOG(GNUNET_ERROR_TYPE_ERROR,
228           _("Failed to setup database at `%s'\n"),
229           plugin->fn);
230       return GNUNET_SYSERR;
231     }
232   return GNUNET_OK;
233 }
234
235
236 /**
237  * Shutdown database connection and associate data
238  * structures.
239  * @param plugin the plugin context (state for this module)
240  */
241 static void
242 database_shutdown(struct Plugin *plugin)
243 {
244   int result;
245   sqlite3_stmt *stmt;
246
247   if (NULL != plugin->store_records)
248     sqlite3_finalize(plugin->store_records);
249   if (NULL != plugin->delete_records)
250     sqlite3_finalize(plugin->delete_records);
251   if (NULL != plugin->iterate_zone)
252     sqlite3_finalize(plugin->iterate_zone);
253   if (NULL != plugin->iterate_all_zones)
254     sqlite3_finalize(plugin->iterate_all_zones);
255   if (NULL != plugin->zone_to_name)
256     sqlite3_finalize(plugin->zone_to_name);
257   if (NULL != plugin->lookup_label)
258     sqlite3_finalize(plugin->lookup_label);
259   result = sqlite3_close(plugin->dbh);
260   if (result == SQLITE_BUSY)
261     {
262       LOG(GNUNET_ERROR_TYPE_WARNING,
263           _("Tried to close sqlite without finalizing all prepared statements.\n"));
264       stmt = sqlite3_next_stmt(plugin->dbh,
265                                NULL);
266       while (NULL != stmt)
267         {
268           GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG,
269                           "sqlite",
270                           "Closing statement %p\n",
271                           stmt);
272           result = sqlite3_finalize(stmt);
273           if (result != SQLITE_OK)
274             GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING,
275                             "sqlite",
276                             "Failed to close statement %p: %d\n",
277                             stmt,
278                             result);
279           stmt = sqlite3_next_stmt(plugin->dbh,
280                                    NULL);
281         }
282       result = sqlite3_close(plugin->dbh);
283     }
284   if (SQLITE_OK != result)
285     LOG_SQLITE(plugin,
286                GNUNET_ERROR_TYPE_ERROR,
287                "sqlite3_close");
288
289   GNUNET_free_non_null(plugin->fn);
290 }
291
292
293 /**
294  * Store a record in the datastore.  Removes any existing record in the
295  * same zone with the same name.
296  *
297  * @param cls closure (internal context for the plugin)
298  * @param zone_key private key of the zone
299  * @param label name that is being mapped (at most 255 characters long)
300  * @param rd_count number of entries in @a rd array
301  * @param rd array of records with data to store
302  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
303  */
304 static int
305 namestore_sqlite_store_records(void *cls,
306                                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
307                                const char *label,
308                                unsigned int rd_count,
309                                const struct GNUNET_GNSRECORD_Data *rd)
310 {
311   struct Plugin *plugin = cls;
312   int n;
313   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
314   uint64_t rvalue;
315   ssize_t data_size;
316
317   memset(&pkey,
318          0,
319          sizeof(pkey));
320   for (unsigned int i = 0; i < rd_count; i++)
321     if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
322       {
323         GNUNET_break(sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) ==
324                      rd[i].data_size);
325         GNUNET_memcpy(&pkey,
326                       rd[i].data,
327                       rd[i].data_size);
328         break;
329       }
330   rvalue = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
331                                     UINT64_MAX);
332   data_size = GNUNET_GNSRECORD_records_get_size(rd_count,
333                                                 rd);
334   if (data_size < 0)
335     {
336       GNUNET_break(0);
337       return GNUNET_SYSERR;
338     }
339   if (data_size > 64 * 65536)
340     {
341       GNUNET_break(0);
342       return GNUNET_SYSERR;
343     }
344   {
345     /* First delete 'old' records */
346     char data[data_size];
347     struct GNUNET_SQ_QueryParam dparams[] = {
348       GNUNET_SQ_query_param_auto_from_type(zone_key),
349       GNUNET_SQ_query_param_string(label),
350       GNUNET_SQ_query_param_end
351     };
352     ssize_t ret;
353
354     ret = GNUNET_GNSRECORD_records_serialize(rd_count,
355                                              rd,
356                                              data_size,
357                                              data);
358     if ((ret < 0) ||
359         (data_size != ret))
360       {
361         GNUNET_break(0);
362         return GNUNET_SYSERR;
363       }
364     if (GNUNET_OK !=
365         GNUNET_SQ_bind(plugin->delete_records,
366                        dparams))
367       {
368         LOG_SQLITE(plugin,
369                    GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
370                    "sqlite3_bind_XXXX");
371         GNUNET_SQ_reset(plugin->dbh,
372                         plugin->delete_records);
373         return GNUNET_SYSERR;
374       }
375     n = sqlite3_step(plugin->delete_records);
376     GNUNET_SQ_reset(plugin->dbh,
377                     plugin->delete_records);
378
379     if (0 != rd_count)
380       {
381         uint32_t rd_count32 = (uint32_t)rd_count;
382         struct GNUNET_SQ_QueryParam sparams[] = {
383           GNUNET_SQ_query_param_auto_from_type(zone_key),
384           GNUNET_SQ_query_param_auto_from_type(&pkey),
385           GNUNET_SQ_query_param_uint64(&rvalue),
386           GNUNET_SQ_query_param_uint32(&rd_count32),
387           GNUNET_SQ_query_param_fixed_size(data, data_size),
388           GNUNET_SQ_query_param_string(label),
389           GNUNET_SQ_query_param_end
390         };
391
392         if (GNUNET_OK !=
393             GNUNET_SQ_bind(plugin->store_records,
394                            sparams))
395           {
396             LOG_SQLITE(plugin,
397                        GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
398                        "sqlite3_bind_XXXX");
399             GNUNET_SQ_reset(plugin->dbh,
400                             plugin->store_records);
401             return GNUNET_SYSERR;
402           }
403         n = sqlite3_step(plugin->store_records);
404         GNUNET_SQ_reset(plugin->dbh,
405                         plugin->store_records);
406       }
407   }
408   switch (n)
409     {
410     case SQLITE_DONE:
411       if (0 != rd_count)
412         GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG,
413                         "sqlite",
414                         "Record stored\n");
415       else
416         GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG,
417                         "sqlite",
418                         "Record deleted\n");
419       return GNUNET_OK;
420
421     case SQLITE_BUSY:
422       LOG_SQLITE(plugin,
423                  GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
424                  "sqlite3_step");
425       return GNUNET_NO;
426
427     default:
428       LOG_SQLITE(plugin,
429                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
430                  "sqlite3_step");
431       return GNUNET_SYSERR;
432     }
433 }
434
435
436 /**
437  * The given 'sqlite' statement has been prepared to be run.
438  * It will return a record which should be given to the iterator.
439  * Runs the statement and parses the returned record.
440  *
441  * @param plugin plugin context
442  * @param stmt to run (and then clean up)
443  * @param zone_key private key of the zone
444  * @param limit maximum number of results to fetch
445  * @param iter iterator to call with the result
446  * @param iter_cls closure for @a iter
447  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
448  */
449 static int
450 get_records_and_call_iterator(struct Plugin *plugin,
451                               sqlite3_stmt *stmt,
452                               const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
453                               uint64_t limit,
454                               GNUNET_NAMESTORE_RecordIterator iter,
455                               void *iter_cls)
456 {
457   int ret;
458   int sret;
459
460   ret = GNUNET_OK;
461   for (uint64_t i = 0; i < limit; i++)
462     {
463       sret = sqlite3_step(stmt);
464
465       if (SQLITE_DONE == sret)
466         {
467           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
468                      "Iteration done (no results)\n");
469           ret = GNUNET_NO;
470           break;
471         }
472       if (SQLITE_ROW != sret)
473         {
474           LOG_SQLITE(plugin,
475                      GNUNET_ERROR_TYPE_ERROR,
476                      "sqlite_step");
477           ret = GNUNET_SYSERR;
478           break;
479         }
480
481       {
482         uint64_t seq;
483         uint32_t record_count;
484         size_t data_size;
485         void *data;
486         char *label;
487         struct GNUNET_CRYPTO_EcdsaPrivateKey zk;
488         struct GNUNET_SQ_ResultSpec rs[] = {
489           GNUNET_SQ_result_spec_uint64(&seq),
490           GNUNET_SQ_result_spec_uint32(&record_count),
491           GNUNET_SQ_result_spec_variable_size(&data,
492                                               &data_size),
493           GNUNET_SQ_result_spec_string(&label),
494           GNUNET_SQ_result_spec_end
495         };
496         struct GNUNET_SQ_ResultSpec rsx[] = {
497           GNUNET_SQ_result_spec_uint64(&seq),
498           GNUNET_SQ_result_spec_uint32(&record_count),
499           GNUNET_SQ_result_spec_variable_size(&data,
500                                               &data_size),
501           GNUNET_SQ_result_spec_string(&label),
502           GNUNET_SQ_result_spec_auto_from_type(&zk),
503           GNUNET_SQ_result_spec_end
504         };
505
506         ret = GNUNET_SQ_extract_result(stmt,
507                                        (NULL == zone_key)
508                                        ? rsx
509                                        : rs);
510         if ((GNUNET_OK != ret) ||
511             (record_count > 64 * 1024))
512           {
513             /* sanity check, don't stack allocate far too much just
514                because database might contain a large value here */
515             GNUNET_break(0);
516             ret = GNUNET_SYSERR;
517             break;
518           }
519         else
520           {
521             struct GNUNET_GNSRECORD_Data rd[record_count];
522
523             GNUNET_assert(0 != seq);
524             if (GNUNET_OK !=
525                 GNUNET_GNSRECORD_records_deserialize(data_size,
526                                                      data,
527                                                      record_count,
528                                                      rd))
529               {
530                 GNUNET_break(0);
531                 ret = GNUNET_SYSERR;
532                 break;
533               }
534             else
535               {
536                 if (NULL != zone_key)
537                   zk = *zone_key;
538                 if (NULL != iter)
539                   iter(iter_cls,
540                        seq,
541                        &zk,
542                        label,
543                        record_count,
544                        rd);
545               }
546           }
547         GNUNET_SQ_cleanup_result(rs);
548       }
549     }
550   GNUNET_SQ_reset(plugin->dbh,
551                   stmt);
552   return ret;
553 }
554
555
556 /**
557  * Lookup records in the datastore for which we are the authority.
558  *
559  * @param cls closure (internal context for the plugin)
560  * @param zone private key of the zone
561  * @param label name of the record in the zone
562  * @param iter function to call with the result
563  * @param iter_cls closure for @a iter
564  * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
565  */
566 static int
567 namestore_sqlite_lookup_records(void *cls,
568                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
569                                 const char *label,
570                                 GNUNET_NAMESTORE_RecordIterator iter,
571                                 void *iter_cls)
572 {
573   struct Plugin *plugin = cls;
574   struct GNUNET_SQ_QueryParam params[] = {
575     GNUNET_SQ_query_param_auto_from_type(zone),
576     GNUNET_SQ_query_param_string(label),
577     GNUNET_SQ_query_param_end
578   };
579
580   if (NULL == zone)
581     {
582       GNUNET_break(0);
583       return GNUNET_SYSERR;
584     }
585   if (GNUNET_OK !=
586       GNUNET_SQ_bind(plugin->lookup_label,
587                      params))
588     {
589       LOG_SQLITE(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
590                  "sqlite3_bind_XXXX");
591       GNUNET_SQ_reset(plugin->dbh,
592                       plugin->lookup_label);
593       return GNUNET_SYSERR;
594     }
595   return get_records_and_call_iterator(plugin,
596                                        plugin->lookup_label,
597                                        zone,
598                                        1,
599                                        iter,
600                                        iter_cls);
601 }
602
603
604 /**
605  * Iterate over the results for a particular key and zone in the
606  * datastore.  Will return at most one result to the iterator.
607  *
608  * @param cls closure (internal context for the plugin)
609  * @param zone hash of public key of the zone, NULL to iterate over all zones
610  * @param serial serial number to exclude in the list of all matching records
611  * @param limit maximum number of results to return
612  * @param iter function to call with the result
613  * @param iter_cls closure for @a iter
614  * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
615  */
616 static int
617 namestore_sqlite_iterate_records(void *cls,
618                                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
619                                  uint64_t serial,
620                                  uint64_t limit,
621                                  GNUNET_NAMESTORE_RecordIterator iter,
622                                  void *iter_cls)
623 {
624   struct Plugin *plugin = cls;
625   sqlite3_stmt *stmt;
626   int err;
627
628   if (NULL == zone)
629     {
630       struct GNUNET_SQ_QueryParam params[] = {
631         GNUNET_SQ_query_param_uint64(&serial),
632         GNUNET_SQ_query_param_uint64(&limit),
633         GNUNET_SQ_query_param_end
634       };
635
636       stmt = plugin->iterate_all_zones;
637       err = GNUNET_SQ_bind(stmt,
638                            params);
639     }
640   else
641     {
642       struct GNUNET_SQ_QueryParam params[] = {
643         GNUNET_SQ_query_param_auto_from_type(zone),
644         GNUNET_SQ_query_param_uint64(&serial),
645         GNUNET_SQ_query_param_uint64(&limit),
646         GNUNET_SQ_query_param_end
647       };
648
649       stmt = plugin->iterate_zone;
650       err = GNUNET_SQ_bind(stmt,
651                            params);
652     }
653   if (GNUNET_OK != err)
654     {
655       LOG_SQLITE(plugin,
656                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
657                  "sqlite3_bind_XXXX");
658       GNUNET_SQ_reset(plugin->dbh,
659                       stmt);
660       return GNUNET_SYSERR;
661     }
662   return get_records_and_call_iterator(plugin,
663                                        stmt,
664                                        zone,
665                                        limit,
666                                        iter,
667                                        iter_cls);
668 }
669
670
671 /**
672  * Look for an existing PKEY delegation record for a given public key.
673  * Returns at most one result to the iterator.
674  *
675  * @param cls closure (internal context for the plugin)
676  * @param zone private key of the zone to look up in, never NULL
677  * @param value_zone public key of the target zone (value), never NULL
678  * @param iter function to call with the result
679  * @param iter_cls closure for @a iter
680  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
681  */
682 static int
683 namestore_sqlite_zone_to_name(void *cls,
684                               const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
685                               const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
686                               GNUNET_NAMESTORE_RecordIterator iter,
687                               void *iter_cls)
688 {
689   struct Plugin *plugin = cls;
690   struct GNUNET_SQ_QueryParam params[] = {
691     GNUNET_SQ_query_param_auto_from_type(zone),
692     GNUNET_SQ_query_param_auto_from_type(value_zone),
693     GNUNET_SQ_query_param_end
694   };
695
696   if (GNUNET_OK !=
697       GNUNET_SQ_bind(plugin->zone_to_name,
698                      params))
699     {
700       LOG_SQLITE(plugin,
701                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
702                  "sqlite3_bind_XXXX");
703       GNUNET_SQ_reset(plugin->dbh,
704                       plugin->zone_to_name);
705       return GNUNET_SYSERR;
706     }
707   LOG(GNUNET_ERROR_TYPE_DEBUG,
708       "Performing reverse lookup for `%s'\n",
709       GNUNET_GNSRECORD_z2s(value_zone));
710   return get_records_and_call_iterator(plugin,
711                                        plugin->zone_to_name,
712                                        zone,
713                                        1,
714                                        iter,
715                                        iter_cls);
716 }
717
718
719 /**
720  * Entry point for the plugin.
721  *
722  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
723  * @return NULL on error, otherwise the plugin context
724  */
725 void *
726 libgnunet_plugin_namestore_sqlite_init(void *cls)
727 {
728   static struct Plugin plugin;
729   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
730   struct GNUNET_NAMESTORE_PluginFunctions *api;
731
732   if (NULL != plugin.cfg)
733     return NULL;                /* can only initialize once! */
734   memset(&plugin,
735          0,
736          sizeof(struct Plugin));
737   plugin.cfg = cfg;
738   if (GNUNET_OK != database_setup(&plugin))
739     {
740       database_shutdown(&plugin);
741       return NULL;
742     }
743   api = GNUNET_new(struct GNUNET_NAMESTORE_PluginFunctions);
744   api->cls = &plugin;
745   api->store_records = &namestore_sqlite_store_records;
746   api->iterate_records = &namestore_sqlite_iterate_records;
747   api->zone_to_name = &namestore_sqlite_zone_to_name;
748   api->lookup_records = &namestore_sqlite_lookup_records;
749   LOG(GNUNET_ERROR_TYPE_INFO,
750       _("Sqlite database running\n"));
751   return api;
752 }
753
754
755 /**
756  * Exit point from the plugin.
757  *
758  * @param cls the plugin context (as returned by "init")
759  * @return always NULL
760  */
761 void *
762 libgnunet_plugin_namestore_sqlite_done(void *cls)
763 {
764   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
765   struct Plugin *plugin = api->cls;
766
767   database_shutdown(plugin);
768   plugin->cfg = NULL;
769   GNUNET_free(api);
770   LOG(GNUNET_ERROR_TYPE_DEBUG,
771       "sqlite plugin is finished\n");
772   return NULL;
773 }
774
775 /* end of plugin_namestore_sqlite.c */