2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016, 2017 GNUnet e.V.
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.
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.
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/>.
20 * @file namecache/plugin_namecache_postgres.c
21 * @brief postgres-based namecache backend
22 * @author Christian Grothoff
25 #include "gnunet_namecache_plugin.h"
26 #include "gnunet_namecache_service.h"
27 #include "gnunet_gnsrecord_lib.h"
28 #include "gnunet_pq_lib.h"
29 #include "namecache.h"
32 #define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
36 * Context for all functions in this plugin.
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
44 * Native Postgres database handle.
52 * Initialize the database connections and associated
53 * data structures (create tables and indices
56 * @param plugin the plugin context (state for this module)
57 * @return #GNUNET_OK on success
60 database_setup (struct Plugin *plugin)
62 struct GNUNET_PQ_ExecuteStatement es_temporary =
63 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks ("
64 " query BYTEA NOT NULL DEFAULT '',"
65 " block BYTEA NOT NULL DEFAULT '',"
66 " expiration_time BIGINT NOT NULL DEFAULT 0"
69 struct GNUNET_PQ_ExecuteStatement es_default =
70 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
71 " query BYTEA NOT NULL DEFAULT '',"
72 " block BYTEA NOT NULL DEFAULT '',"
73 " expiration_time BIGINT NOT NULL DEFAULT 0"
76 const struct GNUNET_PQ_ExecuteStatement *cr;
78 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
79 "namecache-postgres");
80 if (NULL == plugin->dbh)
83 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
95 struct GNUNET_PQ_ExecuteStatement es[] = {
97 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"),
98 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
99 GNUNET_PQ_EXECUTE_STATEMENT_END
103 GNUNET_PQ_exec_statements (plugin->dbh,
106 PQfinish (plugin->dbh);
108 return GNUNET_SYSERR;
113 struct GNUNET_PQ_PreparedStatement ps[] = {
114 GNUNET_PQ_make_prepare ("cache_block",
115 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
117 GNUNET_PQ_make_prepare ("expire_blocks",
118 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1),
119 GNUNET_PQ_make_prepare ("delete_block",
120 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2),
121 GNUNET_PQ_make_prepare ("lookup_block",
122 "SELECT block FROM ns096blocks WHERE query=$1"
123 " ORDER BY expiration_time DESC LIMIT 1", 1),
124 GNUNET_PQ_PREPARED_STATEMENT_END
128 GNUNET_PQ_prepare_statements (plugin->dbh,
131 PQfinish (plugin->dbh);
133 return GNUNET_SYSERR;
142 * Removes any expired block.
144 * @param plugin the plugin
147 namecache_postgres_expire_blocks (struct Plugin *plugin)
149 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
150 struct GNUNET_PQ_QueryParam params[] = {
151 GNUNET_PQ_query_param_absolute_time (&now),
152 GNUNET_PQ_query_param_end
154 enum GNUNET_DB_QueryStatus res;
156 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
159 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
164 * Delete older block in the datastore.
166 * @param plugin the plugin
167 * @param query query for the block
168 * @param expiration_time how old does the block have to be for deletion
171 delete_old_block (struct Plugin *plugin,
172 const struct GNUNET_HashCode *query,
173 struct GNUNET_TIME_AbsoluteNBO expiration_time)
175 struct GNUNET_PQ_QueryParam params[] = {
176 GNUNET_PQ_query_param_auto_from_type (query),
177 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time),
178 GNUNET_PQ_query_param_end
180 enum GNUNET_DB_QueryStatus res;
182 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
185 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
190 * Cache a block in the datastore.
192 * @param cls closure (internal context for the plugin)
193 * @param block block to cache
194 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
197 namecache_postgres_cache_block (void *cls,
198 const struct GNUNET_GNSRECORD_Block *block)
200 struct Plugin *plugin = cls;
201 struct GNUNET_HashCode query;
202 size_t block_size = ntohl (block->purpose.size) +
203 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
204 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
205 struct GNUNET_PQ_QueryParam params[] = {
206 GNUNET_PQ_query_param_auto_from_type (&query),
207 GNUNET_PQ_query_param_fixed_size (block, block_size),
208 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time),
209 GNUNET_PQ_query_param_end
211 enum GNUNET_DB_QueryStatus res;
213 namecache_postgres_expire_blocks (plugin);
214 GNUNET_CRYPTO_hash (&block->derived_key,
215 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
217 if (block_size > 64 * 65536)
220 return GNUNET_SYSERR;
222 delete_old_block (plugin,
224 block->expiration_time);
226 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
230 return GNUNET_SYSERR;
236 * Get the block for a particular zone and label in the
237 * datastore. Will return at most one result to the iterator.
239 * @param cls closure (internal context for the plugin)
240 * @param query hash of public key derived from the zone and the label
241 * @param iter function to call with the result
242 * @param iter_cls closure for @a iter
243 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
246 namecache_postgres_lookup_block (void *cls,
247 const struct GNUNET_HashCode *query,
248 GNUNET_NAMECACHE_BlockCallback iter,
251 struct Plugin *plugin = cls;
253 struct GNUNET_GNSRECORD_Block *block;
254 struct GNUNET_PQ_QueryParam params[] = {
255 GNUNET_PQ_query_param_auto_from_type (query),
256 GNUNET_PQ_query_param_end
258 struct GNUNET_PQ_ResultSpec rs[] = {
259 GNUNET_PQ_result_spec_variable_size ("block",
262 GNUNET_PQ_result_spec_end
264 enum GNUNET_DB_QueryStatus res;
266 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
272 LOG (GNUNET_ERROR_TYPE_WARNING,
273 "Failing lookup block in namecache (postgres error)\n");
274 return GNUNET_SYSERR;
276 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
279 LOG (GNUNET_ERROR_TYPE_DEBUG,
280 "Ending iteration (no more results)\n");
283 if ( (bsize < sizeof (*block)) ||
284 (bsize != ntohl (block->purpose.size) +
285 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
286 sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) )
289 LOG (GNUNET_ERROR_TYPE_DEBUG,
290 "Failing lookup (corrupt block)\n");
291 GNUNET_PQ_cleanup_result (rs);
292 return GNUNET_SYSERR;
296 GNUNET_PQ_cleanup_result (rs);
302 * Shutdown database connection and associate data
305 * @param plugin the plugin context (state for this module)
308 database_shutdown (struct Plugin *plugin)
310 PQfinish (plugin->dbh);
316 * Entry point for the plugin.
318 * @param cls the `struct GNUNET_NAMECACHE_PluginEnvironment *`
319 * @return NULL on error, otherwise the plugin context
322 libgnunet_plugin_namecache_postgres_init (void *cls)
324 static struct Plugin plugin;
325 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
326 struct GNUNET_NAMECACHE_PluginFunctions *api;
328 if (NULL != plugin.cfg)
329 return NULL; /* can only initialize once! */
330 memset (&plugin, 0, sizeof (struct Plugin));
332 if (GNUNET_OK != database_setup (&plugin))
334 database_shutdown (&plugin);
337 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
339 api->cache_block = &namecache_postgres_cache_block;
340 api->lookup_block = &namecache_postgres_lookup_block;
341 LOG (GNUNET_ERROR_TYPE_INFO,
342 "Postgres namecache plugin running\n");
348 * Exit point from the plugin.
350 * @param cls the plugin context (as returned by "init")
351 * @return always NULL
354 libgnunet_plugin_namecache_postgres_done (void *cls)
356 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
357 struct Plugin *plugin = api->cls;
359 database_shutdown (plugin);
362 LOG (GNUNET_ERROR_TYPE_DEBUG,
363 "Postgres namecache plugin is finished\n");
367 /* end of plugin_namecache_postgres.c */