diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-06-11 21:02:30 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-06-11 21:02:30 +0000 |
commit | 589448a17afcc95ef157678d4e1b2eba0f0c9d7a (patch) | |
tree | d9e6c3802d6203680b7b956a60aa0c19dc50a9ff /src | |
parent | d63b9b5f38df2300777e7983af7b0ae210fce626 (diff) | |
download | gnunet-589448a17afcc95ef157678d4e1b2eba0f0c9d7a.tar.gz gnunet-589448a17afcc95ef157678d4e1b2eba0f0c9d7a.zip |
-first draft of postgres namestore plugin (#2157)
Diffstat (limited to 'src')
-rw-r--r-- | src/namestore/Makefile.am | 34 | ||||
-rw-r--r-- | src/namestore/plugin_namestore_postgres.c | 654 | ||||
-rw-r--r-- | src/namestore/plugin_namestore_sqlite.c | 8 |
3 files changed, 689 insertions, 7 deletions
diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am index aea857d74..2106ccf76 100644 --- a/src/namestore/Makefile.am +++ b/src/namestore/Makefile.am | |||
@@ -18,13 +18,18 @@ if USE_COVERAGE | |||
18 | endif | 18 | endif |
19 | 19 | ||
20 | if HAVE_SQLITE | 20 | if HAVE_SQLITE |
21 | SQLITE_TESTS = \ | 21 | SQLITE_PLUGIN = libgnunet_plugin_namestore_sqlite.la |
22 | test_plugin_namestore_sqlite | 22 | SQLITE_TESTS = test_plugin_namestore_sqlite |
23 | endif | ||
24 | if HAVE_POSTGRES | ||
25 | POSTGRES_TESTS = test_plugin_namestore_postgres | ||
26 | POSTGRES_PLUGIN = libgnunet_plugin_namestore_postgres.la | ||
23 | endif | 27 | endif |
24 | 28 | ||
25 | 29 | ||
26 | check_PROGRAMS = \ | 30 | check_PROGRAMS = \ |
27 | $(SQLITE_TESTS) \ | 31 | $(SQLITE_TESTS) \ |
32 | $(POSTGRES_TESTS) \ | ||
28 | test_namestore_record_serialization \ | 33 | test_namestore_record_serialization \ |
29 | test_namestore_api_sign_verify \ | 34 | test_namestore_api_sign_verify \ |
30 | test_namestore_api \ | 35 | test_namestore_api \ |
@@ -87,12 +92,10 @@ gnunet_service_namestore_DEPENDENCIES = \ | |||
87 | $(top_builddir)/src/util/libgnunetutil.la \ | 92 | $(top_builddir)/src/util/libgnunetutil.la \ |
88 | libgnunetnamestore.la | 93 | libgnunetnamestore.la |
89 | 94 | ||
90 | if HAVE_SQLITE | ||
91 | SQLITE_PLUGIN = libgnunet_plugin_namestore_sqlite.la | ||
92 | endif | ||
93 | 95 | ||
94 | plugin_LTLIBRARIES = \ | 96 | plugin_LTLIBRARIES = \ |
95 | $(SQLITE_PLUGIN) | 97 | $(SQLITE_PLUGIN) \ |
98 | $(POSTGRES_PLUGIN) | ||
96 | 99 | ||
97 | libgnunet_plugin_namestore_sqlite_la_SOURCES = \ | 100 | libgnunet_plugin_namestore_sqlite_la_SOURCES = \ |
98 | plugin_namestore_sqlite.c namestore_common.c | 101 | plugin_namestore_sqlite.c namestore_common.c |
@@ -104,6 +107,25 @@ libgnunet_plugin_namestore_sqlite_la_LIBADD = \ | |||
104 | libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ | 107 | libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ |
105 | $(GN_PLUGIN_LDFLAGS) | 108 | $(GN_PLUGIN_LDFLAGS) |
106 | libgnunet_plugin_namestore_sqlite_la_DEPENDENCIES = \ | 109 | libgnunet_plugin_namestore_sqlite_la_DEPENDENCIES = \ |
110 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | ||
111 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
112 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
113 | libgnunetnamestore.la | ||
114 | |||
115 | |||
116 | libgnunet_plugin_namestore_postgres_la_SOURCES = \ | ||
117 | plugin_namestore_postgres.c namestore_common.c | ||
118 | libgnunet_plugin_namestore_postgres_la_LIBADD = \ | ||
119 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | ||
120 | $(top_builddir)/src/postgres/libgnunetpostgres.la \ | ||
121 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
122 | $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lpq \ | ||
123 | $(LTLIBINTL) | ||
124 | libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ | ||
125 | $(GN_PLUGIN_LDFLAGS) | ||
126 | libgnunet_plugin_namestore_postgres_la_DEPENDENCIES = \ | ||
127 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | ||
128 | $(top_builddir)/src/postgres/libgnunetpostgres.la \ | ||
107 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 129 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
108 | $(top_builddir)/src/util/libgnunetutil.la \ | 130 | $(top_builddir)/src/util/libgnunetutil.la \ |
109 | libgnunetnamestore.la | 131 | libgnunetnamestore.la |
diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c new file mode 100644 index 000000000..a60ed8c0a --- /dev/null +++ b/src/namestore/plugin_namestore_postgres.c | |||
@@ -0,0 +1,654 @@ | |||
1 | /* | ||
2 | * This file is part of GNUnet | ||
3 | * (C) 2009, 2011, 2012 Christian Grothoff (and other contributing authors) | ||
4 | * | ||
5 | * GNUnet is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published | ||
7 | * by the Free Software Foundation; either version 3, or (at your | ||
8 | * 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 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with GNUnet; see the file COPYING. If not, write to the | ||
17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | * Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file namestore/plugin_namestore_postgres.c | ||
23 | * @brief postgres-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_postgres_lib.h" | ||
31 | #include "namestore.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * After how many ms "busy" should a DB operation fail for good? | ||
36 | * A low value makes sure that we are more responsive to requests | ||
37 | * (especially PUTs). A high value guarantees a higher success | ||
38 | * rate (SELECTs in iterate can take several seconds despite LIMIT=1). | ||
39 | * | ||
40 | * The default value of 1s should ensure that users do not experience | ||
41 | * huge latencies while at the same time allowing operations to succeed | ||
42 | * with reasonable probability. | ||
43 | */ | ||
44 | #define BUSY_TIMEOUT_MS 1000 | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Log an error message at log-level 'level' that indicates | ||
49 | * a failure of the command 'cmd' on file 'filename' | ||
50 | * with the message given by strerror(errno). | ||
51 | */ | ||
52 | #define LOG_POSTGRES(db, level, cmd) do { GNUNET_log_from (level, "namestore-postgres", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0) | ||
53 | |||
54 | #define LOG(kind,...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__) | ||
55 | |||
56 | |||
57 | /** | ||
58 | * Context for all functions in this plugin. | ||
59 | */ | ||
60 | struct Plugin | ||
61 | { | ||
62 | |||
63 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
64 | |||
65 | /** | ||
66 | * Native Postgres database handle. | ||
67 | */ | ||
68 | PGconn *dbh; | ||
69 | |||
70 | }; | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Create our database indices. | ||
75 | * | ||
76 | * @param dbh handle to the database | ||
77 | */ | ||
78 | static void | ||
79 | create_indices (PGconn * dbh) | ||
80 | { | ||
81 | /* create indices */ | ||
82 | if ( (GNUNET_OK != | ||
83 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone_name_rv ON ns091records (zone_hash,record_name_hash,rvalue)")) || | ||
84 | (GNUNET_OK != | ||
85 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone_delegation ON ns091records (zone_hash,zone_delegation)")) || | ||
86 | (GNUNET_OK != | ||
87 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone_rv ON ns091records (zone_hash,rvalue)")) || | ||
88 | (GNUNET_OK != | ||
89 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone ON ns091records (zone_hash)")) || | ||
90 | (GNUNET_OK != | ||
91 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_name_rv ON ns091records (record_name_hash,rvalue)")) || | ||
92 | (GNUNET_OK != | ||
93 | GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_rv ON ns091records (rvalue)")) ) | ||
94 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
95 | _("Failed to create indices\n")); | ||
96 | } | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Initialize the database connections and associated | ||
101 | * data structures (create tables and indices | ||
102 | * as needed as well). | ||
103 | * | ||
104 | * @param plugin the plugin context (state for this module) | ||
105 | * @return GNUNET_OK on success | ||
106 | */ | ||
107 | static int | ||
108 | database_setup (struct Plugin *plugin) | ||
109 | { | ||
110 | PGresult *res; | ||
111 | |||
112 | plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, | ||
113 | "namestore-postgres"); | ||
114 | if (NULL == plugin->dbh) | ||
115 | return GNUNET_SYSERR; | ||
116 | res = | ||
117 | PQexec (plugin->dbh, | ||
118 | "CREATE TABLE ns091records (" | ||
119 | " zone_key BLOB NOT NULL DEFAULT ''," | ||
120 | " zone_delegation BLOB NOT NULL DEFAULT ''," | ||
121 | " zone_hash BLOB NOT NULL DEFAULT ''," | ||
122 | " record_count INT NOT NULL DEFAULT 0," | ||
123 | " record_data BLOB NOT NULL DEFAULT ''," | ||
124 | " block_expiration_time INT8 NOT NULL DEFAULT 0," | ||
125 | " signature BLOB NOT NULL DEFAULT ''," | ||
126 | " record_name TEXT NOT NULL DEFAULT ''," | ||
127 | " record_name_hash BLOB NOT NULL DEFAULT ''," | ||
128 | " rvalue INT8 NOT NULL DEFAULT ''" | ||
129 | ")" "WITH OIDS"); | ||
130 | if ((NULL == res) || ((PQresultStatus (res) != PGRES_COMMAND_OK) && (0 != strcmp ("42P07", /* duplicate table */ | ||
131 | PQresultErrorField | ||
132 | (res, | ||
133 | PG_DIAG_SQLSTATE))))) | ||
134 | { | ||
135 | (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_COMMAND_OK, "CREATE TABLE", | ||
136 | "ns091records"); | ||
137 | PQfinish (plugin->dbh); | ||
138 | plugin->dbh = NULL; | ||
139 | return GNUNET_SYSERR; | ||
140 | } | ||
141 | if (PQresultStatus (res) == PGRES_COMMAND_OK) | ||
142 | create_indices (plugin->dbh); | ||
143 | PQclear (res); | ||
144 | |||
145 | #define ALL "zone_key, record_name, record_count, record_data, block_expiration_time, signature" | ||
146 | if ((GNUNET_OK != | ||
147 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
148 | "put_records", | ||
149 | "INSERT INTO ns091records (" ALL | ||
150 | ", zone_delegation, zone_hash, record_name_hash, rvalue) VALUES " | ||
151 | "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", 10)) || | ||
152 | (GNUNET_OK != | ||
153 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
154 | "remove_records", | ||
155 | "DELETE FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2", 2)) || | ||
156 | (GNUNET_OK != | ||
157 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
158 | "iterate_records", | ||
159 | "SELECT " ALL | ||
160 | " FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2 ORDER BY rvalue LIMIT 1 OFFSET $3", 3)) || | ||
161 | (GNUNET_OK != | ||
162 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
163 | "iterate_by_zone", | ||
164 | "SELECT " ALL | ||
165 | " FROM ns091records WHERE zone_hash=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) || | ||
166 | (GNUNET_OK != | ||
167 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
168 | "iterate_by_name", | ||
169 | "SELECT " ALL | ||
170 | " FROM ns091records WHERE record_name_hash=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) || | ||
171 | (GNUNET_OK != | ||
172 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
173 | "iterate_all", | ||
174 | "SELECT " ALL | ||
175 | " FROM ns091records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) || | ||
176 | (GNUNET_OK != | ||
177 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
178 | "zone_to_name", | ||
179 | "SELECT " ALL | ||
180 | " FROM ns091records WHERE zone_hash=$1 AND zone_delegation=$2", 2)) || | ||
181 | (GNUNET_OK != | ||
182 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
183 | "delete_zone", | ||
184 | "DELETE FROM ns091records WHERE zone_hash=$1", 1))) | ||
185 | { | ||
186 | PQfinish (plugin->dbh); | ||
187 | plugin->dbh = NULL; | ||
188 | return GNUNET_SYSERR; | ||
189 | } | ||
190 | #undef ALL | ||
191 | return GNUNET_OK; | ||
192 | } | ||
193 | |||
194 | |||
195 | /** | ||
196 | * Removes any existing record in the given zone with the same name. | ||
197 | * | ||
198 | * @param cls closure (internal context for the plugin) | ||
199 | * @param zone hash of the public key of the zone | ||
200 | * @param name name to remove (at most 255 characters long) | ||
201 | * @return GNUNET_OK on success | ||
202 | */ | ||
203 | static int | ||
204 | namestore_postgres_remove_records (void *cls, | ||
205 | const struct GNUNET_CRYPTO_ShortHashCode *zone, | ||
206 | const char *name) | ||
207 | { | ||
208 | struct Plugin *plugin = cls; | ||
209 | PGresult *ret; | ||
210 | struct GNUNET_CRYPTO_ShortHashCode nh; | ||
211 | const char *paramValues[] = { | ||
212 | (const char *) zone, | ||
213 | (const char *) &nh | ||
214 | }; | ||
215 | int paramLengths[] = { | ||
216 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
217 | sizeof (struct GNUNET_CRYPTO_ShortHashCode) | ||
218 | }; | ||
219 | const int paramFormats[] = { 1, 1 }; | ||
220 | size_t name_len; | ||
221 | |||
222 | name_len = strlen (name); | ||
223 | GNUNET_CRYPTO_short_hash (name, name_len, &nh); | ||
224 | ret = | ||
225 | PQexecPrepared (plugin->dbh, "remove_records", 2, paramValues, paramLengths, | ||
226 | paramFormats, 1); | ||
227 | if (GNUNET_OK != | ||
228 | GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put")) | ||
229 | return GNUNET_SYSERR; | ||
230 | PQclear (ret); | ||
231 | return GNUNET_OK; | ||
232 | } | ||
233 | |||
234 | |||
235 | /** | ||
236 | * Store a record in the datastore. Removes any existing record in the | ||
237 | * same zone with the same name. | ||
238 | * | ||
239 | * @param cls closure (internal context for the plugin) | ||
240 | * @param zone_key public key of the zone | ||
241 | * @param expire when does the corresponding block in the DHT expire (until | ||
242 | * when should we never do a DHT lookup for the same name again)? | ||
243 | * @param name name that is being mapped (at most 255 characters long) | ||
244 | * @param rd_count number of entries in 'rd' array | ||
245 | * @param rd array of records with data to store | ||
246 | * @param signature signature of the record block, NULL if signature is unavailable (i.e. | ||
247 | * because the user queried for a particular record type only) | ||
248 | * @return GNUNET_OK on success, else GNUNET_SYSERR | ||
249 | */ | ||
250 | static int | ||
251 | namestore_postgres_put_records (void *cls, | ||
252 | const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key, | ||
253 | struct GNUNET_TIME_Absolute expire, | ||
254 | const char *name, | ||
255 | unsigned int rd_count, | ||
256 | const struct GNUNET_NAMESTORE_RecordData *rd, | ||
257 | const struct GNUNET_CRYPTO_RsaSignature *signature) | ||
258 | { | ||
259 | struct Plugin *plugin = cls; | ||
260 | PGresult *ret; | ||
261 | struct GNUNET_CRYPTO_ShortHashCode zone; | ||
262 | struct GNUNET_CRYPTO_ShortHashCode zone_delegation; | ||
263 | struct GNUNET_CRYPTO_ShortHashCode nh; | ||
264 | size_t name_len; | ||
265 | uint64_t rvalue; | ||
266 | size_t data_size; | ||
267 | unsigned int i; | ||
268 | |||
269 | GNUNET_CRYPTO_short_hash (zone_key, | ||
270 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), | ||
271 | &zone); | ||
272 | (void) namestore_postgres_remove_records (plugin, &zone, name); | ||
273 | name_len = strlen (name); | ||
274 | GNUNET_CRYPTO_short_hash (name, name_len, &nh); | ||
275 | memset (&zone_delegation, 0, sizeof (zone_delegation)); | ||
276 | for (i=0;i<rd_count;i++) | ||
277 | if (rd[i].record_type == GNUNET_NAMESTORE_TYPE_PKEY) | ||
278 | { | ||
279 | GNUNET_assert (sizeof (struct GNUNET_CRYPTO_ShortHashCode) == rd[i].data_size); | ||
280 | memcpy (&zone_delegation, | ||
281 | rd[i].data, | ||
282 | sizeof (struct GNUNET_CRYPTO_ShortHashCode)); | ||
283 | break; | ||
284 | } | ||
285 | rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); | ||
286 | data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd); | ||
287 | if (data_size > 64 * 65536) | ||
288 | { | ||
289 | GNUNET_break (0); | ||
290 | return GNUNET_SYSERR; | ||
291 | } | ||
292 | { | ||
293 | char data[data_size]; | ||
294 | uint64_t expire_be = GNUNET_htonll (expire.abs_value); | ||
295 | uint64_t rvalue_be = GNUNET_htonll (rvalue); | ||
296 | uint32_t rd_count_be = htonl ((uint32_t) rd_count); | ||
297 | const char *paramValues[] = { | ||
298 | (const char *) zone_key, | ||
299 | (const char *) name, | ||
300 | (const char *) &rd_count_be, | ||
301 | (const char *) data, | ||
302 | (const char *) &expire_be, | ||
303 | (const char *) signature, | ||
304 | (const char *) &zone_delegation, | ||
305 | (const char *) &zone, | ||
306 | (const char *) &nh, | ||
307 | (const char *) &rvalue_be | ||
308 | }; | ||
309 | int paramLengths[] = { | ||
310 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), | ||
311 | name_len + 1, | ||
312 | sizeof (uint32_t), | ||
313 | data_size, | ||
314 | sizeof (uint64_t), | ||
315 | sizeof (struct GNUNET_CRYPTO_RsaSignature), | ||
316 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
317 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
318 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
319 | sizeof (uint64_t) | ||
320 | }; | ||
321 | const int paramFormats[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; | ||
322 | |||
323 | if (data_size != GNUNET_NAMESTORE_records_serialize (rd_count, rd, | ||
324 | data_size, data)) | ||
325 | { | ||
326 | GNUNET_break (0); | ||
327 | return GNUNET_SYSERR; | ||
328 | } | ||
329 | ret = | ||
330 | PQexecPrepared (plugin->dbh, "put", 10, paramValues, paramLengths, | ||
331 | paramFormats, 1); | ||
332 | if (GNUNET_OK != | ||
333 | GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put")) | ||
334 | return GNUNET_SYSERR; | ||
335 | PQclear (ret); | ||
336 | } | ||
337 | return GNUNET_OK; | ||
338 | } | ||
339 | |||
340 | |||
341 | /** | ||
342 | * The given 'postgres' result was obtained from the database. | ||
343 | * Parse the record and give it to the iterator. | ||
344 | * | ||
345 | * @param plugin plugin context | ||
346 | * @param stmt_name name of the prepared statement that was executed | ||
347 | * @param res result from postgres to interpret (and then clean up) | ||
348 | * @param iter iterator to call with the result | ||
349 | * @param iter_cls closure for 'iter' | ||
350 | * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error | ||
351 | */ | ||
352 | static int | ||
353 | get_record_and_call_iterator (struct Plugin *plugin, | ||
354 | const char *stmt_name, | ||
355 | PGresult *res, | ||
356 | GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) | ||
357 | { | ||
358 | unsigned int record_count; | ||
359 | size_t data_size; | ||
360 | const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key; | ||
361 | const struct GNUNET_CRYPTO_RsaSignature *sig; | ||
362 | struct GNUNET_TIME_Absolute expiration; | ||
363 | const char *data; | ||
364 | const char *name; | ||
365 | unsigned int cnt; | ||
366 | |||
367 | if (GNUNET_OK != | ||
368 | GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared", | ||
369 | stmt_name)) | ||
370 | { | ||
371 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
372 | "Ending iteration (postgres error)\n"); | ||
373 | return GNUNET_SYSERR; | ||
374 | } | ||
375 | |||
376 | if (0 == (cnt = PQntuples (res))) | ||
377 | { | ||
378 | /* no result */ | ||
379 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
380 | "Ending iteration (no more results)\n"); | ||
381 | PQclear (res); | ||
382 | return GNUNET_NO; | ||
383 | } | ||
384 | GNUNET_assert (1 == cnt); | ||
385 | if ((6 != PQnfields (res)) || | ||
386 | (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) != PQfsize (res, 0)) || | ||
387 | (sizeof (uint32_t) != PQfsize (res, 2)) || | ||
388 | (sizeof (uint64_t) != PQfsize (res, 4)) || | ||
389 | (sizeof (struct GNUNET_CRYPTO_RsaSignature) != PQfsize (res, 5))) | ||
390 | { | ||
391 | GNUNET_break (0); | ||
392 | PQclear (res); | ||
393 | return GNUNET_SYSERR; | ||
394 | } | ||
395 | zone_key = (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *) PQgetvalue (res, 0, 0); | ||
396 | name = PQgetvalue (res, 0, 1); | ||
397 | if ('\0' != name[PQgetlength (res, 0, 1)-1]) | ||
398 | { | ||
399 | GNUNET_break (0); | ||
400 | PQclear (res); | ||
401 | return GNUNET_SYSERR; | ||
402 | } | ||
403 | record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 2)); | ||
404 | data_size = PQgetlength (res, 0, 3); | ||
405 | data = PQgetvalue (res, 0, 3); | ||
406 | expiration.abs_value = | ||
407 | GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 4)); | ||
408 | sig = (const struct GNUNET_CRYPTO_RsaSignature*) PQgetvalue (res, 0, 5); | ||
409 | if (record_count > 64 * 1024) | ||
410 | { | ||
411 | /* sanity check, don't stack allocate far too much just | ||
412 | because database might contain a large value here */ | ||
413 | GNUNET_break (0); | ||
414 | PQclear (res); | ||
415 | return GNUNET_SYSERR; | ||
416 | } | ||
417 | { | ||
418 | struct GNUNET_NAMESTORE_RecordData rd[record_count]; | ||
419 | |||
420 | if (GNUNET_OK != | ||
421 | GNUNET_NAMESTORE_records_deserialize (data_size, data, | ||
422 | record_count, rd)) | ||
423 | { | ||
424 | GNUNET_break (0); | ||
425 | PQclear (res); | ||
426 | return GNUNET_SYSERR; | ||
427 | } | ||
428 | iter (iter_cls, zone_key, expiration, name, | ||
429 | record_count, rd, sig); | ||
430 | } | ||
431 | PQclear (res); | ||
432 | return GNUNET_OK; | ||
433 | } | ||
434 | |||
435 | |||
436 | /** | ||
437 | * Iterate over the results for a particular key and zone in the | ||
438 | * datastore. Will return at most one result to the iterator. | ||
439 | * | ||
440 | * @param cls closure (internal context for the plugin) | ||
441 | * @param zone hash of public key of the zone, NULL to iterate over all zones | ||
442 | * @param name name as string, NULL to iterate over all records of the zone | ||
443 | * @param offset offset in the list of all matching records | ||
444 | * @param iter function to call with the result | ||
445 | * @param iter_cls closure for iter | ||
446 | * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error | ||
447 | */ | ||
448 | static int | ||
449 | namestore_postgres_iterate_records (void *cls, | ||
450 | const struct GNUNET_CRYPTO_ShortHashCode *zone, | ||
451 | const char *name, | ||
452 | uint64_t offset, | ||
453 | GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) | ||
454 | { | ||
455 | struct Plugin *plugin = cls; | ||
456 | const char *stmt_name; | ||
457 | struct GNUNET_CRYPTO_ShortHashCode name_hase; | ||
458 | uint64_t offset_be = GNUNET_htonll (offset); | ||
459 | const char *paramValues[] = { | ||
460 | (const char *) zone, | ||
461 | (const char *) &name_hase, | ||
462 | (const char *) &offset_be, | ||
463 | (const char *) zone, | ||
464 | (const char *) &offset_be, | ||
465 | }; | ||
466 | int paramLengths[] = { | ||
467 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
468 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
469 | sizeof (uint64_t), | ||
470 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
471 | sizeof (uint64_t) | ||
472 | }; | ||
473 | const int paramFormats[] = { 1, 1, 1, 1, 1 }; | ||
474 | unsigned int num_params; | ||
475 | unsigned int first_param; | ||
476 | PGresult *res; | ||
477 | |||
478 | if (NULL == zone) | ||
479 | if (NULL == name) | ||
480 | { | ||
481 | stmt_name = "iterate_all"; | ||
482 | num_params = 1; | ||
483 | first_param = 2; | ||
484 | } | ||
485 | else | ||
486 | { | ||
487 | GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase); | ||
488 | stmt_name = "iterate_by_name"; | ||
489 | num_params = 2; | ||
490 | first_param = 1; | ||
491 | } | ||
492 | else | ||
493 | if (NULL == name) | ||
494 | { | ||
495 | stmt_name = "iterate_by_zone"; | ||
496 | num_params = 2; | ||
497 | first_param = 3; | ||
498 | } | ||
499 | else | ||
500 | { | ||
501 | GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase); | ||
502 | stmt_name = "iterate_records"; | ||
503 | num_params = 3; | ||
504 | first_param = 0; | ||
505 | } | ||
506 | res = | ||
507 | PQexecPrepared (plugin->dbh, stmt_name, num_params, | ||
508 | ¶mValues[first_param], | ||
509 | ¶mLengths[first_param], | ||
510 | ¶mFormats[first_param], 1); | ||
511 | return get_record_and_call_iterator (plugin, stmt_name, res, iter, iter_cls); | ||
512 | } | ||
513 | |||
514 | |||
515 | /** | ||
516 | * Look for an existing PKEY delegation record for a given public key. | ||
517 | * Returns at most one result to the iterator. | ||
518 | * | ||
519 | * @param cls closure (internal context for the plugin) | ||
520 | * @param zone hash of public key of the zone to look up in, never NULL | ||
521 | * @param value_zone hash of the public key of the target zone (value), never NULL | ||
522 | * @param iter function to call with the result | ||
523 | * @param iter_cls closure for iter | ||
524 | * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error | ||
525 | */ | ||
526 | static int | ||
527 | namestore_postgres_zone_to_name (void *cls, | ||
528 | const struct GNUNET_CRYPTO_ShortHashCode *zone, | ||
529 | const struct GNUNET_CRYPTO_ShortHashCode *value_zone, | ||
530 | GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) | ||
531 | { | ||
532 | struct Plugin *plugin = cls; | ||
533 | const char *paramValues[] = { | ||
534 | (const char *) zone, | ||
535 | (const char *) value_zone | ||
536 | }; | ||
537 | int paramLengths[] = { | ||
538 | sizeof (struct GNUNET_CRYPTO_ShortHashCode), | ||
539 | sizeof (struct GNUNET_CRYPTO_ShortHashCode) | ||
540 | }; | ||
541 | const int paramFormats[] = { 1, 1 }; | ||
542 | PGresult *res; | ||
543 | |||
544 | res = | ||
545 | PQexecPrepared (plugin->dbh, "zone_to_name", 2, | ||
546 | paramValues, paramLengths, paramFormats, 1); | ||
547 | return get_record_and_call_iterator (plugin, "zone_to_name", res, iter, iter_cls); | ||
548 | } | ||
549 | |||
550 | |||
551 | /** | ||
552 | * Delete an entire zone (all records). Not used in normal operation. | ||
553 | * | ||
554 | * @param cls closure (internal context for the plugin) | ||
555 | * @param zone zone to delete | ||
556 | */ | ||
557 | static void | ||
558 | namestore_postgres_delete_zone (void *cls, | ||
559 | const struct GNUNET_CRYPTO_ShortHashCode *zone) | ||
560 | { | ||
561 | struct Plugin *plugin = cls; | ||
562 | PGresult *ret; | ||
563 | const char *paramValues[] = { | ||
564 | (const char *) zone, | ||
565 | }; | ||
566 | int paramLengths[] = { | ||
567 | sizeof (struct GNUNET_CRYPTO_ShortHashCode) | ||
568 | }; | ||
569 | const int paramFormats[] = { 1 }; | ||
570 | |||
571 | ret = | ||
572 | PQexecPrepared (plugin->dbh, "delete_zone", 1, paramValues, paramLengths, | ||
573 | paramFormats, 1); | ||
574 | if (GNUNET_OK != | ||
575 | GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "delete_zone")) | ||
576 | { | ||
577 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
578 | "Deleting zone failed!\n"); | ||
579 | return; | ||
580 | } | ||
581 | PQclear (ret); | ||
582 | } | ||
583 | |||
584 | |||
585 | /** | ||
586 | * Shutdown database connection and associate data | ||
587 | * structures. | ||
588 | * | ||
589 | * @param plugin the plugin context (state for this module) | ||
590 | */ | ||
591 | static void | ||
592 | database_shutdown (struct Plugin *plugin) | ||
593 | { | ||
594 | PQfinish (plugin->dbh); | ||
595 | plugin->dbh = NULL; | ||
596 | } | ||
597 | |||
598 | |||
599 | /** | ||
600 | * Entry point for the plugin. | ||
601 | * | ||
602 | * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" | ||
603 | * @return NULL on error, othrewise the plugin context | ||
604 | */ | ||
605 | void * | ||
606 | libgnunet_plugin_namestore_postgres_init (void *cls) | ||
607 | { | ||
608 | static struct Plugin plugin; | ||
609 | const struct GNUNET_CONFIGURATION_Handle *cfg = cls; | ||
610 | struct GNUNET_NAMESTORE_PluginFunctions *api; | ||
611 | |||
612 | if (NULL != plugin.cfg) | ||
613 | return NULL; /* can only initialize once! */ | ||
614 | memset (&plugin, 0, sizeof (struct Plugin)); | ||
615 | plugin.cfg = cfg; | ||
616 | if (GNUNET_OK != database_setup (&plugin)) | ||
617 | { | ||
618 | database_shutdown (&plugin); | ||
619 | return NULL; | ||
620 | } | ||
621 | api = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_PluginFunctions)); | ||
622 | api->cls = &plugin; | ||
623 | api->put_records = &namestore_postgres_put_records; | ||
624 | api->remove_records = &namestore_postgres_remove_records; | ||
625 | api->iterate_records = &namestore_postgres_iterate_records; | ||
626 | api->zone_to_name = &namestore_postgres_zone_to_name; | ||
627 | api->delete_zone = &namestore_postgres_delete_zone; | ||
628 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
629 | _("Postgres database running\n")); | ||
630 | return api; | ||
631 | } | ||
632 | |||
633 | |||
634 | /** | ||
635 | * Exit point from the plugin. | ||
636 | * | ||
637 | * @param cls the plugin context (as returned by "init") | ||
638 | * @return always NULL | ||
639 | */ | ||
640 | void * | ||
641 | libgnunet_plugin_namestore_postgres_done (void *cls) | ||
642 | { | ||
643 | struct GNUNET_NAMESTORE_PluginFunctions *api = cls; | ||
644 | struct Plugin *plugin = api->cls; | ||
645 | |||
646 | database_shutdown (plugin); | ||
647 | plugin->cfg = NULL; | ||
648 | GNUNET_free (api); | ||
649 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
650 | "postgres plugin is finished\n"); | ||
651 | return NULL; | ||
652 | } | ||
653 | |||
654 | /* end of plugin_namestore_postgres.c */ | ||
diff --git a/src/namestore/plugin_namestore_sqlite.c b/src/namestore/plugin_namestore_sqlite.c index f6858876e..b3f57033a 100644 --- a/src/namestore/plugin_namestore_sqlite.c +++ b/src/namestore/plugin_namestore_sqlite.c | |||
@@ -591,6 +591,13 @@ get_record_and_call_iterator (struct Plugin *plugin, | |||
591 | GNUNET_break (0); | 591 | GNUNET_break (0); |
592 | ret = GNUNET_SYSERR; | 592 | ret = GNUNET_SYSERR; |
593 | } | 593 | } |
594 | else if (record_count > 64 * 1024) | ||
595 | { | ||
596 | /* sanity check, don't stack allocate far too much just | ||
597 | because database might contain a large value here */ | ||
598 | GNUNET_break (0); | ||
599 | ret = GNUNET_SYSERR; | ||
600 | } | ||
594 | else | 601 | else |
595 | { | 602 | { |
596 | struct GNUNET_NAMESTORE_RecordData rd[record_count]; | 603 | struct GNUNET_NAMESTORE_RecordData rd[record_count]; |
@@ -601,7 +608,6 @@ get_record_and_call_iterator (struct Plugin *plugin, | |||
601 | { | 608 | { |
602 | GNUNET_break (0); | 609 | GNUNET_break (0); |
603 | ret = GNUNET_SYSERR; | 610 | ret = GNUNET_SYSERR; |
604 | record_count = 0; | ||
605 | } | 611 | } |
606 | else | 612 | else |
607 | { | 613 | { |