aboutsummaryrefslogtreecommitdiff
path: root/src/plugin/namecache/plugin_namecache_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugin/namecache/plugin_namecache_postgres.c')
-rw-r--r--src/plugin/namecache/plugin_namecache_postgres.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/plugin/namecache/plugin_namecache_postgres.c b/src/plugin/namecache/plugin_namecache_postgres.c
new file mode 100644
index 000000000..7e2925d1a
--- /dev/null
+++ b/src/plugin/namecache/plugin_namecache_postgres.c
@@ -0,0 +1,315 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016, 2017, 2022 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/plugin_namecache_postgres.c
23 * @brief postgres-based namecache backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_namecache_plugin.h"
28#include "gnunet_namecache_service.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_pq_lib.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
34
35
36/**
37 * Context for all functions in this plugin.
38 */
39struct Plugin
40{
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Postgres database handle.
45 */
46 struct GNUNET_PQ_Context *dbh;
47};
48
49
50/**
51 * Initialize the database connections and associated
52 * data structures (create tables and indices
53 * as needed as well).
54 *
55 * @param plugin the plugin context (state for this module)
56 * @return #GNUNET_OK on success
57 */
58static enum GNUNET_GenericReturnValue
59database_setup (struct Plugin *plugin)
60{
61 struct GNUNET_PQ_PreparedStatement ps[] = {
62 GNUNET_PQ_make_prepare ("cache_block",
63 "INSERT INTO namecache.ns096blocks"
64 " (query, block, expiration_time)"
65 " VALUES"
66 " ($1, $2, $3)"),
67 GNUNET_PQ_make_prepare ("expire_blocks",
68 "DELETE FROM namecache.ns096blocks"
69 " WHERE expiration_time<$1"),
70 GNUNET_PQ_make_prepare ("delete_block",
71 "DELETE FROM namecache.ns096blocks"
72 " WHERE query=$1 AND expiration_time<=$2"),
73 GNUNET_PQ_make_prepare ("lookup_block",
74 "SELECT block"
75 " FROM namecache.ns096blocks"
76 " WHERE query=$1"
77 " ORDER BY expiration_time DESC LIMIT 1"),
78 GNUNET_PQ_PREPARED_STATEMENT_END
79 };
80
81 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
82 "namecache-postgres",
83 "namecache-",
84 NULL,
85 ps);
86 if (NULL == plugin->dbh)
87 return GNUNET_SYSERR;
88 return GNUNET_OK;
89}
90
91
92/**
93 * Removes any expired block.
94 *
95 * @param plugin the plugin
96 */
97static void
98namecache_postgres_expire_blocks (struct Plugin *plugin)
99{
100 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
101 struct GNUNET_PQ_QueryParam params[] = {
102 GNUNET_PQ_query_param_absolute_time (&now),
103 GNUNET_PQ_query_param_end
104 };
105 enum GNUNET_DB_QueryStatus res;
106
107 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
108 "expire_blocks",
109 params);
110 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
111}
112
113
114/**
115 * Delete older block in the datastore.
116 *
117 * @param plugin the plugin
118 * @param query query for the block
119 * @param expiration_time how old does the block have to be for deletion
120 */
121static void
122delete_old_block (struct Plugin *plugin,
123 const struct GNUNET_HashCode *query,
124 struct GNUNET_TIME_Absolute expiration_time)
125{
126 struct GNUNET_PQ_QueryParam params[] = {
127 GNUNET_PQ_query_param_auto_from_type (query),
128 GNUNET_PQ_query_param_absolute_time (&expiration_time),
129 GNUNET_PQ_query_param_end
130 };
131 enum GNUNET_DB_QueryStatus res;
132
133 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
134 "delete_block",
135 params);
136 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
137}
138
139
140/**
141 * Cache a block in the datastore.
142 *
143 * @param cls closure (internal context for the plugin)
144 * @param block block to cache
145 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
146 */
147static enum GNUNET_GenericReturnValue
148namecache_postgres_cache_block (void *cls,
149 const struct GNUNET_GNSRECORD_Block *block)
150{
151 struct Plugin *plugin = cls;
152 struct GNUNET_HashCode query;
153 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
154 struct GNUNET_TIME_Absolute exp;
155 exp = GNUNET_GNSRECORD_block_get_expiration (block);
156 struct GNUNET_PQ_QueryParam params[] = {
157 GNUNET_PQ_query_param_auto_from_type (&query),
158 GNUNET_PQ_query_param_fixed_size (block, block_size),
159 GNUNET_PQ_query_param_absolute_time (&exp),
160 GNUNET_PQ_query_param_end
161 };
162 enum GNUNET_DB_QueryStatus res;
163
164 namecache_postgres_expire_blocks (plugin);
165 GNUNET_GNSRECORD_query_from_block (block,
166 &query);
167 if (block_size > 64 * 65536)
168 {
169 GNUNET_break (0);
170 return GNUNET_SYSERR;
171 }
172 delete_old_block (plugin,
173 &query,
174 exp);
175
176 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
177 "cache_block",
178 params);
179 if (0 > res)
180 return GNUNET_SYSERR;
181 return GNUNET_OK;
182}
183
184
185/**
186 * Get the block for a particular zone and label in the
187 * datastore. Will return at most one result to the iterator.
188 *
189 * @param cls closure (internal context for the plugin)
190 * @param query hash of public key derived from the zone and the label
191 * @param iter function to call with the result
192 * @param iter_cls closure for @a iter
193 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
194 */
195static enum GNUNET_GenericReturnValue
196namecache_postgres_lookup_block (void *cls,
197 const struct GNUNET_HashCode *query,
198 GNUNET_NAMECACHE_BlockCallback iter,
199 void *iter_cls)
200{
201 struct Plugin *plugin = cls;
202 size_t bsize;
203 struct GNUNET_GNSRECORD_Block *block;
204 struct GNUNET_PQ_QueryParam params[] = {
205 GNUNET_PQ_query_param_auto_from_type (query),
206 GNUNET_PQ_query_param_end
207 };
208 struct GNUNET_PQ_ResultSpec rs[] = {
209 GNUNET_PQ_result_spec_variable_size ("block",
210 (void **) &block,
211 &bsize),
212 GNUNET_PQ_result_spec_end
213 };
214 enum GNUNET_DB_QueryStatus res;
215
216 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
217 "lookup_block",
218 params,
219 rs);
220 if (0 > res)
221 {
222 LOG (GNUNET_ERROR_TYPE_WARNING,
223 "Failing lookup block in namecache (postgres error)\n");
224 return GNUNET_SYSERR;
225 }
226 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
227 {
228 /* no result */
229 LOG (GNUNET_ERROR_TYPE_DEBUG,
230 "Ending iteration (no more results)\n");
231 return GNUNET_NO;
232 }
233 if ((bsize < sizeof(*block)))
234 {
235 GNUNET_break (0);
236 LOG (GNUNET_ERROR_TYPE_DEBUG,
237 "Failing lookup (corrupt block)\n");
238 GNUNET_PQ_cleanup_result (rs);
239 return GNUNET_SYSERR;
240 }
241 iter (iter_cls,
242 block);
243 GNUNET_PQ_cleanup_result (rs);
244 return GNUNET_OK;
245}
246
247
248/**
249 * Shutdown database connection and associate data
250 * structures.
251 *
252 * @param plugin the plugin context (state for this module)
253 */
254static void
255database_shutdown (struct Plugin *plugin)
256{
257 GNUNET_PQ_disconnect (plugin->dbh);
258 plugin->dbh = NULL;
259}
260
261
262/**
263 * Entry point for the plugin.
264 *
265 * @param cls the `struct GNUNET_NAMECACHE_PluginEnvironment *`
266 * @return NULL on error, otherwise the plugin context
267 */
268void *
269libgnunet_plugin_namecache_postgres_init (void *cls)
270{
271 static struct Plugin plugin;
272 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
273 struct GNUNET_NAMECACHE_PluginFunctions *api;
274
275 if (NULL != plugin.cfg)
276 return NULL; /* can only initialize once! */
277 memset (&plugin, 0, sizeof(struct Plugin));
278 plugin.cfg = cfg;
279 if (GNUNET_OK != database_setup (&plugin))
280 {
281 database_shutdown (&plugin);
282 return NULL;
283 }
284 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
285 api->cls = &plugin;
286 api->cache_block = &namecache_postgres_cache_block;
287 api->lookup_block = &namecache_postgres_lookup_block;
288 LOG (GNUNET_ERROR_TYPE_INFO,
289 "Postgres namecache plugin running\n");
290 return api;
291}
292
293
294/**
295 * Exit point from the plugin.
296 *
297 * @param cls the plugin context (as returned by "init")
298 * @return always NULL
299 */
300void *
301libgnunet_plugin_namecache_postgres_done (void *cls)
302{
303 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
304 struct Plugin *plugin = api->cls;
305
306 database_shutdown (plugin);
307 plugin->cfg = NULL;
308 GNUNET_free (api);
309 LOG (GNUNET_ERROR_TYPE_DEBUG,
310 "Postgres namecache plugin is finished\n");
311 return NULL;
312}
313
314
315/* end of plugin_namecache_postgres.c */