aboutsummaryrefslogtreecommitdiff
path: root/src/namecache
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-10-16 19:32:52 +0000
committerChristian Grothoff <christian@grothoff.org>2013-10-16 19:32:52 +0000
commit6308c2556c54ea8a19b33bfe16bd2f81eae65e86 (patch)
tree2017381fa55744868e3664b59a46d60cce8c2433 /src/namecache
parente71d2567fc6d2634c503587ba481cc92f5f5e60e (diff)
downloadgnunet-6308c2556c54ea8a19b33bfe16bd2f81eae65e86.tar.gz
gnunet-6308c2556c54ea8a19b33bfe16bd2f81eae65e86.zip
-copied block-related functions from namestore to namecache
Diffstat (limited to 'src/namecache')
-rw-r--r--src/namecache/Makefile.am164
-rw-r--r--src/namecache/gnunet-namecache.c252
-rw-r--r--src/namecache/gnunet-service-namecache.c429
-rw-r--r--src/namecache/namecache.conf.in22
-rw-r--r--src/namecache/namecache.h152
-rw-r--r--src/namecache/namecache_api.c738
-rw-r--r--src/namecache/plugin_namecache_postgres.c440
-rw-r--r--src/namecache/plugin_namecache_sqlite.c608
-rw-r--r--src/namecache/test_namecache_api.conf19
-rw-r--r--src/namecache/test_namecache_api_cache_block.c239
-rw-r--r--src/namecache/test_plugin_namecache.c131
-rw-r--r--src/namecache/test_plugin_namecache_postgres.conf3
-rw-r--r--src/namecache/test_plugin_namecache_sqlite.conf2
13 files changed, 3199 insertions, 0 deletions
diff --git a/src/namecache/Makefile.am b/src/namecache/Makefile.am
new file mode 100644
index 000000000..b8ad9b7fa
--- /dev/null
+++ b/src/namecache/Makefile.am
@@ -0,0 +1,164 @@
1AM_CPPFLAGS = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5pkgcfgdir= $(pkgdatadir)/config.d/
6
7libexecdir= $(pkglibdir)/libexec/
8
9pkgcfg_DATA = \
10 namecache.conf
11
12
13if MINGW
14 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
15endif
16
17if USE_COVERAGE
18 AM_CFLAGS = --coverage -O0
19 XLIBS = -lgcov
20endif
21
22if HAVE_SQLITE
23SQLITE_PLUGIN = libgnunet_plugin_namecache_sqlite.la
24if HAVE_TESTING
25SQLITE_TESTS = test_plugin_namecache_sqlite
26endif
27endif
28
29if HAVE_POSTGRES
30POSTGRES_PLUGIN = libgnunet_plugin_namecache_postgres.la
31if HAVE_TESTING
32POSTGRES_TESTS = test_plugin_namecache_postgres
33endif
34endif
35
36# testcases do not even build yet; thus: experimental!
37if HAVE_TESTING
38TESTING_TESTS = \
39 test_namecache_api_cache_block
40endif
41
42if HAVE_SQLITE
43check_PROGRAMS = \
44 $(SQLITE_TESTS) \
45 $(POSTGRES_TESTS) \
46 $(TESTING_TESTS)
47endif
48
49if ENABLE_TEST_RUN
50TESTS = \
51 $(check_PROGRAMS)
52endif
53
54lib_LTLIBRARIES = \
55 libgnunetnamecache.la
56
57
58libgnunetnamecache_la_SOURCES = \
59 namecache_api.c \
60 namecache.h
61libgnunetnamecache_la_LIBADD = \
62 $(top_builddir)/src/util/libgnunetutil.la \
63 $(GN_LIBINTL)
64libgnunetnamecache_la_LDFLAGS = \
65 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
66 -version-info 0:0:0
67
68
69libexec_PROGRAMS = \
70 gnunet-service-namecache
71
72bin_PROGRAMS = \
73 gnunet-namecache
74
75gnunet_namecache_SOURCES = \
76 gnunet-namecache.c
77gnunet_namecache_LDADD = \
78 $(top_builddir)/src/namestore/libgnunetnamestore.la \
79 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
80 $(top_builddir)/src/util/libgnunetutil.la \
81 libgnunetnamecache.la \
82 $(GN_LIBINTL)
83gnunet_namecache_DEPENDENCIES = \
84 $(top_builddir)/src/identity/libgnunetidentity.la \
85 $(top_builddir)/src/util/libgnunetutil.la \
86 libgnunetnamecache.la
87
88
89gnunet_service_namecache_SOURCES = \
90 gnunet-service-namecache.c
91
92gnunet_service_namecache_LDADD = \
93 $(top_builddir)/src/namestore/libgnunetnamestore.la \
94 $(top_builddir)/src/statistics/libgnunetstatistics.la \
95 $(top_builddir)/src/util/libgnunetutil.la \
96 libgnunetnamecache.la \
97 $(GN_LIBINTL)
98gnunet_service_namecache_DEPENDENCIES = \
99 $(top_builddir)/src/statistics/libgnunetstatistics.la \
100 $(top_builddir)/src/util/libgnunetutil.la \
101 libgnunetnamecache.la
102
103
104plugin_LTLIBRARIES = \
105 $(SQLITE_PLUGIN) \
106 $(POSTGRES_PLUGIN)
107
108libgnunet_plugin_namecache_sqlite_la_SOURCES = \
109 plugin_namecache_sqlite.c
110libgnunet_plugin_namecache_sqlite_la_LIBADD = \
111 $(top_builddir)/src/namecache/libgnunetnamecache.la \
112 $(top_builddir)/src/statistics/libgnunetstatistics.la \
113 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
114 $(LTLIBINTL)
115libgnunet_plugin_namecache_sqlite_la_LDFLAGS = \
116 $(GN_PLUGIN_LDFLAGS)
117libgnunet_plugin_namecache_sqlite_la_DEPENDENCIES = \
118 $(top_builddir)/src/statistics/libgnunetstatistics.la \
119 $(top_builddir)/src/util/libgnunetutil.la \
120 libgnunetnamecache.la
121
122
123libgnunet_plugin_namecache_postgres_la_SOURCES = \
124 plugin_namecache_postgres.c
125libgnunet_plugin_namecache_postgres_la_LIBADD = \
126 $(top_builddir)/src/namecache/libgnunetnamecache.la \
127 $(top_builddir)/src/postgres/libgnunetpostgres.la \
128 $(top_builddir)/src/statistics/libgnunetstatistics.la \
129 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lpq \
130 $(LTLIBINTL)
131libgnunet_plugin_namecache_postgres_la_LDFLAGS = \
132 $(GN_PLUGIN_LDFLAGS)
133libgnunet_plugin_namecache_postgres_la_DEPENDENCIES = \
134 $(top_builddir)/src/postgres/libgnunetpostgres.la \
135 $(top_builddir)/src/statistics/libgnunetstatistics.la \
136 $(top_builddir)/src/util/libgnunetutil.la \
137 libgnunetnamecache.la
138
139test_namecache_api_cache_block_SOURCES = \
140 test_namecache_api_cache_block.c
141test_namecache_api_cache_block_LDADD = \
142 $(top_builddir)/src/namestore/libgnunetnamestore.la \
143 $(top_builddir)/src/namecache/libgnunetnamecache.la \
144 $(top_builddir)/src/testing/libgnunettesting.la \
145 $(top_builddir)/src/util/libgnunetutil.la
146
147test_plugin_namecache_sqlite_SOURCES = \
148 test_plugin_namecache.c
149test_plugin_namecache_sqlite_LDADD = \
150 $(top_builddir)/src/testing/libgnunettesting.la \
151 $(top_builddir)/src/util/libgnunetutil.la
152
153test_plugin_namecache_postgres_SOURCES = \
154 test_plugin_namecache.c
155test_plugin_namecache_postgres_LDADD = \
156 $(top_builddir)/src/testing/libgnunettesting.la \
157 $(top_builddir)/src/util/libgnunetutil.la
158
159EXTRA_DIST = \
160 test_namecache_api.conf \
161 test_plugin_namecache_sqlite.conf \
162 test_plugin_namecache_postgres.conf \
163 test_namecache_defaults.conf
164
diff --git a/src/namecache/gnunet-namecache.c b/src/namecache/gnunet-namecache.c
new file mode 100644
index 000000000..cda40fae9
--- /dev/null
+++ b/src/namecache/gnunet-namecache.c
@@ -0,0 +1,252 @@
1
2/*
3 This file is part of GNUnet.
4 (C) 2012, 2013 Christian Grothoff (and other contributing authors)
5
6 GNUnet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 3, or (at your
9 option) any later version.
10
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20*/
21/**
22 * @file gnunet-namecache.c
23 * @brief command line tool to inspect the name cache
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - test
28 */
29#include "platform.h"
30#include <gnunet_util_lib.h>
31#include <gnunet_dnsparser_lib.h>
32#include <gnunet_identity_service.h>
33#include <gnunet_gnsrecord_lib.h>
34#include <gnunet_namecache_service.h>
35#include <gnunet_namestore_service.h>
36
37
38/**
39 * Handle to the namecache.
40 */
41static struct GNUNET_NAMECACHE_Handle *ns;
42
43/**
44 * Queue entry for the 'query' operation.
45 */
46static struct GNUNET_NAMECACHE_QueueEntry *qe;
47
48/**
49 * Name (label) of the records to list.
50 */
51static char *name;
52
53/**
54 * Public key of the zone to look in.
55 */
56static struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
57
58/**
59 * Public key of the zone to look in, in ASCII.
60 */
61static char *pkey;
62
63/**
64 * Global return value
65 */
66static int ret;
67
68
69/**
70 * Task run on shutdown. Cleans up everything.
71 *
72 * @param cls unused
73 * @param tc scheduler context
74 */
75static void
76do_shutdown (void *cls,
77 const struct GNUNET_SCHEDULER_TaskContext *tc)
78{
79 if (NULL != qe)
80 {
81 GNUNET_NAMECACHE_cancel (qe);
82 qe = NULL;
83 }
84 if (NULL != ns)
85 {
86 GNUNET_NAMECACHE_disconnect (ns);
87 ns = NULL;
88 }
89}
90
91
92/**
93 * Process a record that was stored in the namecache in a block.
94 *
95 * @param cls closure, NULL
96 * @param rd_len number of entries in @a rd array
97 * @param rd array of records with data to store
98 */
99static void
100display_records_from_block (void *cls,
101 unsigned int rd_len,
102 const struct GNUNET_NAMESTORE_RecordData *rd)
103{
104 const char *typestring;
105 char *s;
106 unsigned int i;
107
108 if (0 == rd_len)
109 {
110 FPRINTF (stdout,
111 _("No records found for `%s'"),
112 name);
113 return;
114 }
115 FPRINTF (stdout,
116 "%s:\n",
117 name);
118 for (i=0;i<rd_len;i++)
119 {
120 typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
121 s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
122 rd[i].data,
123 rd[i].data_size);
124 if (NULL == s)
125 {
126 FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
127 (unsigned int) rd[i].record_type);
128 continue;
129 }
130 FPRINTF (stdout,
131 "\t%s: %s\n",
132 typestring,
133 s);
134 GNUNET_free (s);
135 }
136 FPRINTF (stdout, "%s", "\n");
137}
138
139
140/**
141 * Display block obtained from listing (by name).
142 *
143 * @param cls NULL
144 * @param block NULL if not found
145 */
146static void
147handle_block (void *cls,
148 const struct GNUNET_NAMESTORE_Block *block)
149{
150 qe = NULL;
151 if (NULL == block)
152 {
153 fprintf (stderr,
154 "No matching block found\n");
155 }
156 else if (GNUNET_OK !=
157 GNUNET_NAMESTORE_block_decrypt (block,
158 &pubkey,
159 name,
160 &display_records_from_block,
161 NULL))
162 {
163 fprintf (stderr,
164 "Failed to decrypt block!\n");
165 }
166 GNUNET_SCHEDULER_shutdown ();
167}
168
169
170/**
171 * Main function that will be run.
172 *
173 * @param cls closure
174 * @param args remaining command-line arguments
175 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
176 * @param cfg configuration
177 */
178static void
179run (void *cls, char *const *args, const char *cfgfile,
180 const struct GNUNET_CONFIGURATION_Handle *cfg)
181{
182 struct GNUNET_HashCode dhash;
183
184 if (NULL == pkey)
185 {
186 fprintf (stderr,
187 _("You must specify which zone should be accessed\n"));
188 return;
189 }
190
191 if (GNUNET_OK !=
192 GNUNET_CRYPTO_ecdsa_public_key_from_string (pkey,
193 strlen (pkey),
194 &pubkey))
195 {
196 fprintf (stderr,
197 _("Invalid public key for reverse lookup `%s'\n"),
198 pkey);
199 GNUNET_SCHEDULER_shutdown ();
200 return;
201 }
202 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
203 &do_shutdown,
204 NULL);
205 GNUNET_NAMESTORE_query_from_public_key (&pubkey,
206 name,
207 &dhash);
208 qe = GNUNET_NAMECACHE_lookup_block (ns,
209 &dhash,
210 &handle_block,
211 NULL);
212}
213
214
215/**
216 * The main function for gnunet-namecache.
217 *
218 * @param argc number of arguments from the command line
219 * @param argv command line arguments
220 * @return 0 ok, 1 on error
221 */
222int
223main (int argc, char *const *argv)
224{
225 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
226 {'n', "name", "NAME",
227 gettext_noop ("name of the record to add/delete/display"), 1,
228 &GNUNET_GETOPT_set_string, &name},
229 {'z', "zone", "PKEY",
230 gettext_noop ("spezifies the public key of the zone to look in"), 1,
231 &GNUNET_GETOPT_set_string, &pkey},
232 GNUNET_GETOPT_OPTION_END
233 };
234
235 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
236 return 2;
237
238 GNUNET_log_setup ("gnunet-namecache", "WARNING", NULL);
239 if (GNUNET_OK !=
240 GNUNET_PROGRAM_run (argc, argv, "gnunet-namecache",
241 _("GNUnet zone manipulation tool"),
242 options,
243 &run, NULL))
244 {
245 GNUNET_free ((void*) argv);
246 return 1;
247 }
248 GNUNET_free ((void*) argv);
249 return ret;
250}
251
252/* end of gnunet-namecache.c */
diff --git a/src/namecache/gnunet-service-namecache.c b/src/namecache/gnunet-service-namecache.c
new file mode 100644
index 000000000..832f02b45
--- /dev/null
+++ b/src/namecache/gnunet-service-namecache.c
@@ -0,0 +1,429 @@
1/*
2 This file is part of GNUnet.
3 (C) 2012, 2013 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 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_namecache_service.h"
31#include "gnunet_namecache_plugin.h"
32#include "gnunet_signatures.h"
33#include "namecache.h"
34
35#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
36
37
38/**
39 * A namecache client
40 */
41struct NamecacheClient;
42
43
44/**
45 * A namecache client
46 */
47struct NamecacheClient
48{
49 /**
50 * Next element in the DLL
51 */
52 struct NamecacheClient *next;
53
54 /**
55 * Previous element in the DLL
56 */
57 struct NamecacheClient *prev;
58
59 /**
60 * The client
61 */
62 struct GNUNET_SERVER_Client *client;
63
64};
65
66
67/**
68 * Configuration handle.
69 */
70static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg;
71
72/**
73 * Database handle
74 */
75static struct GNUNET_NAMECACHE_PluginFunctions *GSN_database;
76
77/**
78 * Name of the database plugin
79 */
80static char *db_lib_name;
81
82/**
83 * Our notification context.
84 */
85static struct GNUNET_SERVER_NotificationContext *snc;
86
87/**
88 * Head of the Client DLL
89 */
90static struct NamecacheClient *client_head;
91
92/**
93 * Tail of the Client DLL
94 */
95static struct NamecacheClient *client_tail;
96
97/**
98 * Notification context shared by all monitors.
99 */
100static struct GNUNET_SERVER_NotificationContext *monitor_nc;
101
102
103
104/**
105 * Task run during shutdown.
106 *
107 * @param cls unused
108 * @param tc unused
109 */
110static void
111cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
112{
113 struct NamecacheClient *nc;
114
115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
116 "Stopping namecache service\n");
117 if (NULL != snc)
118 {
119 GNUNET_SERVER_notification_context_destroy (snc);
120 snc = NULL;
121 }
122 while (NULL != (nc = client_head))
123 {
124 GNUNET_CONTAINER_DLL_remove (client_head, client_tail, nc);
125 GNUNET_SERVER_client_set_user_context (nc->client, NULL);
126 GNUNET_free (nc);
127 }
128 GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, GSN_database));
129 GNUNET_free (db_lib_name);
130 db_lib_name = NULL;
131 if (NULL != monitor_nc)
132 {
133 GNUNET_SERVER_notification_context_destroy (monitor_nc);
134 monitor_nc = NULL;
135 }
136}
137
138
139/**
140 * Called whenever a client is disconnected.
141 * Frees our resources associated with that client.
142 *
143 * @param cls closure
144 * @param client identification of the client
145 */
146static void
147client_disconnect_notification (void *cls,
148 struct GNUNET_SERVER_Client *client)
149{
150 struct NamecacheClient *nc;
151
152 if (NULL == client)
153 return;
154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
155 "Client %p disconnected\n",
156 client);
157 if (NULL == (nc = GNUNET_SERVER_client_get_user_context (client, struct NamecacheClient)))
158 return;
159 GNUNET_CONTAINER_DLL_remove (client_head, client_tail, nc);
160 GNUNET_free (nc);
161}
162
163
164/**
165 * Add a client to our list of active clients, if it is not yet
166 * in there.
167 *
168 * @param client client to add
169 * @return internal namecache client structure for this client
170 */
171static struct NamecacheClient *
172client_lookup (struct GNUNET_SERVER_Client *client)
173{
174 struct NamecacheClient *nc;
175
176 nc = GNUNET_SERVER_client_get_user_context (client, struct NamecacheClient);
177 if (NULL != nc)
178 return nc;
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "Client %p connected\n",
181 client);
182 nc = GNUNET_new (struct NamecacheClient);
183 nc->client = client;
184 GNUNET_SERVER_notification_context_add (snc, client);
185 GNUNET_CONTAINER_DLL_insert (client_head, client_tail, nc);
186 GNUNET_SERVER_client_set_user_context (client, nc);
187 return nc;
188}
189
190
191/**
192 * Context for name lookups passed from #handle_lookup_block to
193 * #handle_lookup_block_it as closure
194 */
195struct LookupBlockContext
196{
197 /**
198 * The client to send the response to
199 */
200 struct NamecacheClient *nc;
201
202 /**
203 * Operation id for the name lookup
204 */
205 uint32_t request_id;
206
207};
208
209
210/**
211 * A #GNUNET_NAMECACHE_BlockCallback for name lookups in #handle_lookup_block
212 *
213 * @param cls a `struct LookupNameContext *` with information about the request
214 * @param block the block
215 */
216static void
217handle_lookup_block_it (void *cls,
218 const struct GNUNET_NAMESTORE_Block *block)
219{
220 struct LookupBlockContext *lnc = cls;
221 struct LookupBlockResponseMessage *r;
222 size_t esize;
223
224 esize = ntohl (block->purpose.size)
225 - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)
226 - sizeof (struct GNUNET_TIME_AbsoluteNBO);
227 r = GNUNET_malloc (sizeof (struct LookupBlockResponseMessage) + esize);
228 r->gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
229 r->gns_header.header.size = htons (sizeof (struct LookupBlockResponseMessage) + esize);
230 r->gns_header.r_id = htonl (lnc->request_id);
231 r->expire = block->expiration_time;
232 r->signature = block->signature;
233 r->derived_key = block->derived_key;
234 memcpy (&r[1], &block[1], esize);
235 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
236 "Sending `%s' message with expiration time %s\n",
237 "NAMECACHE_LOOKUP_BLOCK_RESPONSE",
238 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (r->expire)));
239 GNUNET_SERVER_notification_context_unicast (snc,
240 lnc->nc->client,
241 &r->gns_header.header,
242 GNUNET_NO);
243 GNUNET_free (r);
244}
245
246
247/**
248 * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK message
249 *
250 * @param cls unused
251 * @param client client sending the message
252 * @param message message of type 'struct LookupNameMessage'
253 */
254static void
255handle_lookup_block (void *cls,
256 struct GNUNET_SERVER_Client *client,
257 const struct GNUNET_MessageHeader *message)
258{
259 const struct LookupBlockMessage *ln_msg;
260 struct LookupBlockContext lnc;
261 struct NamecacheClient *nc;
262 struct LookupBlockResponseMessage zir_end;
263 int ret;
264
265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
266 "Received `%s' message\n",
267 "NAMECACHE_LOOKUP_BLOCK");
268 nc = client_lookup(client);
269 ln_msg = (const struct LookupBlockMessage *) message;
270 lnc.request_id = ntohl (ln_msg->gns_header.r_id);
271 lnc.nc = nc;
272 if (GNUNET_SYSERR ==
273 (ret = GSN_database->lookup_block (GSN_database->cls,
274 &ln_msg->query,
275 &handle_lookup_block_it, &lnc)))
276 {
277 /* internal error (in database plugin); might be best to just hang up on
278 plugin rather than to signal that there are 'no' results, which
279 might also be false... */
280 GNUNET_break (0);
281 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
282 return;
283 }
284 if (0 == ret)
285 {
286 /* no records match at all, generate empty response */
287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288 "Sending empty `%s' message\n",
289 "NAMECACHE_LOOKUP_BLOCK_RESPONSE");
290 memset (&zir_end, 0, sizeof (zir_end));
291 zir_end.gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE);
292 zir_end.gns_header.header.size = htons (sizeof (struct LookupBlockResponseMessage));
293 zir_end.gns_header.r_id = ln_msg->gns_header.r_id;
294 GNUNET_SERVER_notification_context_unicast (snc,
295 client,
296 &zir_end.gns_header.header,
297 GNUNET_NO);
298
299 }
300 GNUNET_SERVER_receive_done (client, GNUNET_OK);
301}
302
303
304/**
305 * Handles a #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE message
306 *
307 * @param cls unused
308 * @param client client sending the message
309 * @param message message of type 'struct BlockCacheMessage'
310 */
311static void
312handle_block_cache (void *cls,
313 struct GNUNET_SERVER_Client *client,
314 const struct GNUNET_MessageHeader *message)
315{
316 struct NamecacheClient *nc;
317 const struct BlockCacheMessage *rp_msg;
318 struct BlockCacheResponseMessage rpr_msg;
319 struct GNUNET_NAMESTORE_Block *block;
320 size_t esize;
321 int res;
322
323 nc = client_lookup (client);
324 if (ntohs (message->size) < sizeof (struct BlockCacheMessage))
325 {
326 GNUNET_break (0);
327 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
328 return;
329 }
330 rp_msg = (const struct BlockCacheMessage *) message;
331 esize = ntohs (rp_msg->gns_header.header.size) - sizeof (struct BlockCacheMessage);
332 block = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_Block) + esize);
333 block->signature = rp_msg->signature;
334 block->derived_key = rp_msg->derived_key;
335 block->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
336 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
337 esize);
338 block->expiration_time = rp_msg->expire;
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340 "Received `%s' message with expiration time %s\n",
341 "NAMECACHE_BLOCK_CACHE",
342 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (block->expiration_time)));
343 memcpy (&block[1], &rp_msg[1], esize);
344 res = GSN_database->cache_block (GSN_database->cls,
345 block);
346 GNUNET_free (block);
347
348 rpr_msg.gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE);
349 rpr_msg.gns_header.header.size = htons (sizeof (struct BlockCacheResponseMessage));
350 rpr_msg.gns_header.r_id = rp_msg->gns_header.r_id;
351 rpr_msg.op_result = htonl (res);
352 GNUNET_SERVER_notification_context_unicast (snc,
353 nc->client,
354 &rpr_msg.gns_header.header,
355 GNUNET_NO);
356 GNUNET_SERVER_receive_done (client, GNUNET_OK);
357}
358
359
360/**
361 * Process namecache requests.
362 *
363 * @param cls closure
364 * @param server the initialized server
365 * @param cfg configuration to use
366 */
367static void
368run (void *cls, struct GNUNET_SERVER_Handle *server,
369 const struct GNUNET_CONFIGURATION_Handle *cfg)
370{
371 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
372 {&handle_lookup_block, NULL,
373 GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK, sizeof (struct LookupBlockMessage)},
374 {&handle_block_cache, NULL,
375 GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE, 0},
376 {NULL, NULL, 0, 0}
377 };
378 char *database;
379
380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting namecache service\n");
381 GSN_cfg = cfg;
382 monitor_nc = GNUNET_SERVER_notification_context_create (server, 1);
383
384 /* Loading database plugin */
385 if (GNUNET_OK !=
386 GNUNET_CONFIGURATION_get_value_string (cfg, "namecache", "database",
387 &database))
388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n");
389
390 GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_namecache_%s", database);
391 GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) GSN_cfg);
392 GNUNET_free (database);
393 if (NULL == GSN_database)
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 "Could not load database backend `%s'\n",
397 db_lib_name);
398 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
399 return;
400 }
401
402 /* Configuring server handles */
403 GNUNET_SERVER_add_handlers (server, handlers);
404 snc = GNUNET_SERVER_notification_context_create (server, 16);
405 GNUNET_SERVER_disconnect_notify (server,
406 &client_disconnect_notification,
407 NULL);
408 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
409 NULL);
410}
411
412
413/**
414 * The main function for the template service.
415 *
416 * @param argc number of arguments from the command line
417 * @param argv command line arguments
418 * @return 0 ok, 1 on error
419 */
420int
421main (int argc, char *const *argv)
422{
423 return (GNUNET_OK ==
424 GNUNET_SERVICE_run (argc, argv, "namecache",
425 GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
426}
427
428/* end of gnunet-service-namecache.c */
429
diff --git a/src/namecache/namecache.conf.in b/src/namecache/namecache.conf.in
new file mode 100644
index 000000000..7464a9a9f
--- /dev/null
+++ b/src/namecache/namecache.conf.in
@@ -0,0 +1,22 @@
1[namecache]
2AUTOSTART = YES
3USER_SERVICE = YES
4UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-namecache.sock
5UNIX_MATCH_UID = NO
6UNIX_MATCH_GID = YES
7@UNIXONLY@ PORT = 2099
8HOSTNAME = localhost
9BINARY = gnunet-service-namecache
10ACCEPT_FROM = 127.0.0.1;
11ACCEPT_FROM6 = ::1;
12DATABASE = sqlite
13
14[namecache-sqlite]
15FILENAME = $GNUNET_DATA_HOME/namecache/sqlite.db
16
17[namecache-postgres]
18CONFIG = connect_timeout=10; dbname=gnunet
19TEMPORARY_TABLE = NO
20
21
22
diff --git a/src/namecache/namecache.h b/src/namecache/namecache.h
new file mode 100644
index 000000000..cc81946e2
--- /dev/null
+++ b/src/namecache/namecache.h
@@ -0,0 +1,152 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011-2013 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 namecache/namecache.h
23 * @brief common internal definitions for namecache service
24 * @author Matthias Wachs
25 * @author Christian Grothoff
26 */
27#ifndef NAMECACHE_H
28#define NAMECACHE_H
29
30/**
31 * Maximum length of any name, including 0-termination.
32 */
33#define MAX_NAME_LEN 256
34
35GNUNET_NETWORK_STRUCT_BEGIN
36
37/**
38 * Generic namecache message with op id
39 */
40struct GNUNET_NAMECACHE_Header
41{
42 /**
43 * header.type will be GNUNET_MESSAGE_TYPE_NAMECACHE_*
44 * header.size will be message size
45 */
46 struct GNUNET_MessageHeader header;
47
48 /**
49 * Request ID in NBO
50 */
51 uint32_t r_id GNUNET_PACKED;
52};
53
54
55/**
56 * Lookup a block in the namecache
57 */
58struct LookupBlockMessage
59{
60 /**
61 * Type will be #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK
62 */
63 struct GNUNET_NAMECACHE_Header gns_header;
64
65 /**
66 * The query.
67 */
68 struct GNUNET_HashCode query GNUNET_PACKED;
69
70};
71
72
73/**
74 * Lookup response
75 */
76struct LookupBlockResponseMessage
77{
78 /**
79 * Type will be #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE
80 */
81 struct GNUNET_NAMECACHE_Header gns_header;
82
83 /**
84 * Expiration time
85 */
86 struct GNUNET_TIME_AbsoluteNBO expire;
87
88 /**
89 * Signature.
90 */
91 struct GNUNET_CRYPTO_EcdsaSignature signature;
92
93 /**
94 * Derived public key.
95 */
96 struct GNUNET_CRYPTO_EcdsaPublicKey derived_key;
97
98 /* follwed by encrypted block data */
99};
100
101
102/**
103 * Cache a record in the namecache.
104 */
105struct BlockCacheMessage
106{
107 /**
108 * Type will be #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE
109 */
110 struct GNUNET_NAMECACHE_Header gns_header;
111
112 /**
113 * Expiration time
114 */
115 struct GNUNET_TIME_AbsoluteNBO expire;
116
117 /**
118 * Signature.
119 */
120 struct GNUNET_CRYPTO_EcdsaSignature signature;
121
122 /**
123 * Derived public key.
124 */
125 struct GNUNET_CRYPTO_EcdsaPublicKey derived_key;
126
127 /* follwed by encrypted block data */
128};
129
130
131/**
132 * Response to a request to cache a block.
133 */
134struct BlockCacheResponseMessage
135{
136 /**
137 * Type will be #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE
138 */
139 struct GNUNET_NAMECACHE_Header gns_header;
140
141 /**
142 * #GNUNET_OK on success, #GNUNET_SYSERR error
143 */
144 int32_t op_result GNUNET_PACKED;
145};
146
147
148GNUNET_NETWORK_STRUCT_END
149
150
151/* end of namecache.h */
152#endif
diff --git a/src/namecache/namecache_api.c b/src/namecache/namecache_api.c
new file mode 100644
index 000000000..a28ed7d53
--- /dev/null
+++ b/src/namecache/namecache_api.c
@@ -0,0 +1,738 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010-2013 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 namecache/namecache_api.c
23 * @brief API to access the NAMECACHE service
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_crypto_lib.h"
32#include "gnunet_constants.h"
33#include "gnunet_dnsparser_lib.h"
34#include "gnunet_arm_service.h"
35#include "gnunet_signatures.h"
36#include "gnunet_namecache_service.h"
37#include "gnunet_namestore_service.h"
38#include "namecache.h"
39
40
41#define LOG(kind,...) GNUNET_log_from (kind, "namecache-api",__VA_ARGS__)
42
43
44/**
45 * An QueueEntry used to store information for a pending
46 * NAMECACHE record operation
47 */
48struct GNUNET_NAMECACHE_QueueEntry
49{
50
51 /**
52 * Kept in a DLL.
53 */
54 struct GNUNET_NAMECACHE_QueueEntry *next;
55
56 /**
57 * Kept in a DLL.
58 */
59 struct GNUNET_NAMECACHE_QueueEntry *prev;
60
61 /**
62 * Main handle to access the namecache.
63 */
64 struct GNUNET_NAMECACHE_Handle *nsh;
65
66 /**
67 * Continuation to call
68 */
69 GNUNET_NAMECACHE_ContinuationWithStatus cont;
70
71 /**
72 * Closure for @e cont.
73 */
74 void *cont_cls;
75
76 /**
77 * Function to call with the blocks we get back; or NULL.
78 */
79 GNUNET_NAMESTORE_BlockProcessor block_proc;
80
81 /**
82 * Closure for @e block_proc.
83 */
84 void *block_proc_cls;
85
86 /**
87 * The operation id this zone iteration operation has
88 */
89 uint32_t op_id;
90
91};
92
93
94/**
95 * Message in linked list we should send to the service. The
96 * actual binary message follows this struct.
97 */
98struct PendingMessage
99{
100
101 /**
102 * Kept in a DLL.
103 */
104 struct PendingMessage *next;
105
106 /**
107 * Kept in a DLL.
108 */
109 struct PendingMessage *prev;
110
111 /**
112 * Size of the message.
113 */
114 size_t size;
115
116};
117
118
119/**
120 * Connection to the NAMECACHE service.
121 */
122struct GNUNET_NAMECACHE_Handle
123{
124
125 /**
126 * Configuration to use.
127 */
128 const struct GNUNET_CONFIGURATION_Handle *cfg;
129
130 /**
131 * Socket (if available).
132 */
133 struct GNUNET_CLIENT_Connection *client;
134
135 /**
136 * Currently pending transmission request (or NULL).
137 */
138 struct GNUNET_CLIENT_TransmitHandle *th;
139
140 /**
141 * Head of linked list of pending messages to send to the service
142 */
143 struct PendingMessage *pending_head;
144
145 /**
146 * Tail of linked list of pending messages to send to the service
147 */
148 struct PendingMessage *pending_tail;
149
150 /**
151 * Head of pending namecache queue entries
152 */
153 struct GNUNET_NAMECACHE_QueueEntry *op_head;
154
155 /**
156 * Tail of pending namecache queue entries
157 */
158 struct GNUNET_NAMECACHE_QueueEntry *op_tail;
159
160 /**
161 * Reconnect task
162 */
163 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
164
165 /**
166 * Delay introduced before we reconnect.
167 */
168 struct GNUNET_TIME_Relative reconnect_delay;
169
170 /**
171 * Should we reconnect to service due to some serious error?
172 */
173 int reconnect;
174
175 /**
176 * Did we start to receive yet?
177 */
178 int is_receiving;
179
180 /**
181 * The last operation id used for a NAMECACHE operation
182 */
183 uint32_t last_op_id_used;
184
185};
186
187
188/**
189 * Disconnect from service and then reconnect.
190 *
191 * @param h our handle
192 */
193static void
194force_reconnect (struct GNUNET_NAMECACHE_Handle *h);
195
196
197/**
198 * Handle an incoming message of type
199 * #GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE.
200 *
201 * @param qe the respective entry in the message queue
202 * @param msg the message we received
203 * @param size the message size
204 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and we did NOT notify the client
205 */
206static int
207handle_lookup_block_response (struct GNUNET_NAMECACHE_QueueEntry *qe,
208 const struct LookupBlockResponseMessage *msg,
209 size_t size)
210{
211 struct GNUNET_NAMESTORE_Block *block;
212 char buf[size + sizeof (struct GNUNET_NAMESTORE_Block)
213 - sizeof (struct LookupBlockResponseMessage)];
214
215 LOG (GNUNET_ERROR_TYPE_DEBUG,
216 "Received `%s'\n",
217 "LOOKUP_BLOCK_RESPONSE");
218 if (0 == GNUNET_TIME_absolute_ntoh (msg->expire).abs_value_us)
219 {
220 /* no match found */
221 if (NULL != qe->block_proc)
222 qe->block_proc (qe->block_proc_cls, NULL);
223 return GNUNET_OK;
224 }
225
226 block = (struct GNUNET_NAMESTORE_Block *) buf;
227 block->signature = msg->signature;
228 block->derived_key = msg->derived_key;
229 block->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
230 block->purpose.size = htonl (size - sizeof (struct LookupBlockResponseMessage) +
231 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
232 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose));
233 block->expiration_time = msg->expire;
234 memcpy (&block[1],
235 &msg[1],
236 size - sizeof (struct LookupBlockResponseMessage));
237 if (GNUNET_OK !=
238 GNUNET_NAMESTORE_block_verify (block))
239 {
240 GNUNET_break (0);
241 return GNUNET_SYSERR;
242 }
243 if (NULL != qe->block_proc)
244 qe->block_proc (qe->block_proc_cls, block);
245 else
246 GNUNET_break (0);
247 return GNUNET_OK;
248}
249
250
251/**
252 * Handle an incoming message of type
253 * #GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE
254 *
255 * @param qe the respective entry in the message queue
256 * @param msg the message we received
257 * @param size the message size
258 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and we did NOT notify the client
259 */
260static int
261handle_block_cache_response (struct GNUNET_NAMECACHE_QueueEntry *qe,
262 const struct BlockCacheResponseMessage *msg,
263 size_t size)
264{
265 int res;
266
267 LOG (GNUNET_ERROR_TYPE_DEBUG,
268 "Received `%s'\n",
269 "BLOCK_CACHE_RESPONSE");
270 res = ntohl (msg->op_result);
271 /* TODO: add actual error message from namecache to response... */
272 if (NULL != qe->cont)
273 qe->cont (qe->cont_cls,
274 res,
275 (GNUNET_OK == res) ?
276 NULL
277 : _("Namecache failed to cache block"));
278 return GNUNET_OK;
279}
280
281
282/**
283 * Handle incoming messages for record operations
284 *
285 * @param qe the respective zone iteration handle
286 * @param msg the message we received
287 * @param type the message type in host byte order
288 * @param size the message size
289 * @return #GNUNET_OK on success, #GNUNET_NO if we notified the client about
290 * the error, #GNUNET_SYSERR on error and we did NOT notify the client
291 */
292static int
293manage_record_operations (struct GNUNET_NAMECACHE_QueueEntry *qe,
294 const struct GNUNET_MessageHeader *msg,
295 uint16_t type,
296 size_t size)
297{
298 /* handle different message type */
299 switch (type)
300 {
301 case GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK_RESPONSE:
302 if (size < sizeof (struct LookupBlockResponseMessage))
303 {
304 GNUNET_break (0);
305 return GNUNET_SYSERR;
306 }
307 return handle_lookup_block_response (qe, (const struct LookupBlockResponseMessage *) msg, size);
308 case GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE_RESPONSE:
309 if (size != sizeof (struct BlockCacheResponseMessage))
310 {
311 GNUNET_break (0);
312 return GNUNET_SYSERR;
313 }
314 return handle_block_cache_response (qe, (const struct BlockCacheResponseMessage *) msg, size);
315 default:
316 GNUNET_break (0);
317 return GNUNET_SYSERR;
318 }
319}
320
321
322/**
323 * Type of a function to call when we receive a message
324 * from the service.
325 *
326 * @param cls the `struct GNUNET_NAMECACHE_SchedulingHandle`
327 * @param msg message received, NULL on timeout or fatal error
328 */
329static void
330process_namecache_message (void *cls,
331 const struct GNUNET_MessageHeader *msg)
332{
333 struct GNUNET_NAMECACHE_Handle *h = cls;
334 const struct GNUNET_NAMECACHE_Header *gm;
335 struct GNUNET_NAMECACHE_QueueEntry *qe;
336 uint16_t size;
337 uint16_t type;
338 uint32_t r_id;
339 int ret;
340
341 if (NULL == msg)
342 {
343 force_reconnect (h);
344 return;
345 }
346 size = ntohs (msg->size);
347 type = ntohs (msg->type);
348 if (size < sizeof (struct GNUNET_NAMECACHE_Header))
349 {
350 GNUNET_break_op (0);
351 GNUNET_CLIENT_receive (h->client,
352 &process_namecache_message, h,
353 GNUNET_TIME_UNIT_FOREVER_REL);
354 return;
355 }
356 gm = (const struct GNUNET_NAMECACHE_Header *) msg;
357 r_id = ntohl (gm->r_id);
358
359 LOG (GNUNET_ERROR_TYPE_DEBUG,
360 "Received message type %u size %u op %u\n",
361 (unsigned int) type,
362 (unsigned int) size,
363 (unsigned int) r_id);
364
365 /* Is it a record related operation ? */
366 for (qe = h->op_head; qe != NULL; qe = qe->next)
367 if (qe->op_id == r_id)
368 break;
369 if (NULL != qe)
370 {
371 ret = manage_record_operations (qe, msg, type, size);
372 if (GNUNET_SYSERR == ret)
373 {
374 /* protocol error, need to reconnect */
375 h->reconnect = GNUNET_YES;
376 }
377 else
378 {
379 /* client was notified about success or failure, clean up 'qe' */
380 GNUNET_CONTAINER_DLL_remove (h->op_head,
381 h->op_tail,
382 qe);
383 GNUNET_free (qe);
384 }
385 }
386 if (GNUNET_YES == h->reconnect)
387 {
388 force_reconnect (h);
389 return;
390 }
391 GNUNET_CLIENT_receive (h->client, &process_namecache_message, h,
392 GNUNET_TIME_UNIT_FOREVER_REL);
393}
394
395
396/**
397 * Transmit messages from the message queue to the service
398 * (if there are any, and if we are not already trying).
399 *
400 * @param h handle to use
401 */
402static void
403do_transmit (struct GNUNET_NAMECACHE_Handle *h);
404
405
406/**
407 * We can now transmit a message to NAMECACHE. Do it.
408 *
409 * @param cls the `struct GNUNET_NAMECACHE_Handle`
410 * @param size number of bytes we can transmit
411 * @param buf where to copy the messages
412 * @return number of bytes copied into @a buf
413 */
414static size_t
415transmit_message_to_namecache (void *cls,
416 size_t size,
417 void *buf)
418{
419 struct GNUNET_NAMECACHE_Handle *h = cls;
420 struct PendingMessage *p;
421 size_t ret;
422 char *cbuf;
423
424 h->th = NULL;
425 if ((0 == size) || (NULL == buf))
426 {
427 force_reconnect (h);
428 return 0;
429 }
430 ret = 0;
431 cbuf = buf;
432 while ( (NULL != (p = h->pending_head)) &&
433 (p->size <= size) )
434 {
435 memcpy (&cbuf[ret], &p[1], p->size);
436 ret += p->size;
437 size -= p->size;
438 GNUNET_CONTAINER_DLL_remove (h->pending_head,
439 h->pending_tail,
440 p);
441 if (GNUNET_NO == h->is_receiving)
442 {
443 h->is_receiving = GNUNET_YES;
444 GNUNET_CLIENT_receive (h->client,
445 &process_namecache_message, h,
446 GNUNET_TIME_UNIT_FOREVER_REL);
447 }
448 GNUNET_free (p);
449 }
450 do_transmit (h);
451 return ret;
452}
453
454
455/**
456 * Transmit messages from the message queue to the service
457 * (if there are any, and if we are not already trying).
458 *
459 * @param h handle to use
460 */
461static void
462do_transmit (struct GNUNET_NAMECACHE_Handle *h)
463{
464 struct PendingMessage *p;
465
466 if (NULL != h->th)
467 return; /* transmission request already pending */
468 if (NULL == (p = h->pending_head))
469 return; /* transmission queue empty */
470 if (NULL == h->client)
471 return; /* currently reconnecting */
472 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, p->size,
473 GNUNET_TIME_UNIT_FOREVER_REL,
474 GNUNET_NO, &transmit_message_to_namecache,
475 h);
476 GNUNET_break (NULL != h->th);
477}
478
479
480/**
481 * Reconnect to namecache service.
482 *
483 * @param h the handle to the NAMECACHE service
484 */
485static void
486reconnect (struct GNUNET_NAMECACHE_Handle *h)
487{
488 GNUNET_assert (NULL == h->client);
489 h->client = GNUNET_CLIENT_connect ("namecache", h->cfg);
490 GNUNET_assert (NULL != h->client);
491 do_transmit (h);
492}
493
494
495/**
496 * Re-establish the connection to the service.
497 *
498 * @param cls handle to use to re-connect.
499 * @param tc scheduler context
500 */
501static void
502reconnect_task (void *cls,
503 const struct GNUNET_SCHEDULER_TaskContext *tc)
504{
505 struct GNUNET_NAMECACHE_Handle *h = cls;
506
507 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
508 reconnect (h);
509}
510
511
512/**
513 * Disconnect from service and then reconnect.
514 *
515 * @param h our handle
516 */
517static void
518force_reconnect (struct GNUNET_NAMECACHE_Handle *h)
519{
520 if (NULL != h->th)
521 {
522 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
523 h->th = NULL;
524 }
525 h->reconnect = GNUNET_NO;
526 GNUNET_CLIENT_disconnect (h->client);
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Reconnecting to namecache\n");
529 h->is_receiving = GNUNET_NO;
530 h->client = NULL;
531 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
532 h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
533 &reconnect_task,
534 h);
535}
536
537
538/**
539 * Get a fresh operation id to distinguish between namecache requests
540 *
541 * @param h the namecache handle
542 * @return next operation id to use
543 */
544static uint32_t
545get_op_id (struct GNUNET_NAMECACHE_Handle *h)
546{
547 return h->last_op_id_used++;
548}
549
550
551/**
552 * Initialize the connection with the NAMECACHE service.
553 *
554 * @param cfg configuration to use
555 * @return handle to the GNS service, or NULL on error
556 */
557struct GNUNET_NAMECACHE_Handle *
558GNUNET_NAMECACHE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
559{
560 struct GNUNET_NAMECACHE_Handle *h;
561
562 h = GNUNET_new (struct GNUNET_NAMECACHE_Handle);
563 h->cfg = cfg;
564 h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect_task, h);
565 h->last_op_id_used = 0;
566 return h;
567}
568
569
570/**
571 * Disconnect from the namecache service (and free associated
572 * resources).
573 *
574 * @param h handle to the namecache
575 */
576void
577GNUNET_NAMECACHE_disconnect (struct GNUNET_NAMECACHE_Handle *h)
578{
579 struct PendingMessage *p;
580 struct GNUNET_NAMECACHE_QueueEntry *q;
581
582 LOG (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
583 GNUNET_assert (NULL != h);
584 if (NULL != h->th)
585 {
586 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
587 h->th = NULL;
588 }
589 while (NULL != (p = h->pending_head))
590 {
591 GNUNET_CONTAINER_DLL_remove (h->pending_head, h->pending_tail, p);
592 GNUNET_free (p);
593 }
594 GNUNET_break (NULL == h->op_head);
595 while (NULL != (q = h->op_head))
596 {
597 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, q);
598 GNUNET_free (q);
599 }
600 if (NULL != h->client)
601 {
602 GNUNET_CLIENT_disconnect (h->client);
603 h->client = NULL;
604 }
605 if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
606 {
607 GNUNET_SCHEDULER_cancel (h->reconnect_task);
608 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
609 }
610 GNUNET_free (h);
611}
612
613
614/**
615 * Store an item in the namecache. If the item is already present,
616 * it is replaced with the new record.
617 *
618 * @param h handle to the namecache
619 * @param block block to store
620 * @param cont continuation to call when done
621 * @param cont_cls closure for cont
622 * @return handle to abort the request
623 */
624struct GNUNET_NAMECACHE_QueueEntry *
625GNUNET_NAMECACHE_block_cache (struct GNUNET_NAMECACHE_Handle *h,
626 const struct GNUNET_NAMESTORE_Block *block,
627 GNUNET_NAMECACHE_ContinuationWithStatus cont,
628 void *cont_cls)
629{
630 struct GNUNET_NAMECACHE_QueueEntry *qe;
631 struct PendingMessage *pe;
632 struct BlockCacheMessage *msg;
633 uint32_t rid;
634 size_t blen;
635 size_t msg_size;
636
637 GNUNET_assert (NULL != h);
638 blen = ntohl (block->purpose.size)
639 - sizeof (struct GNUNET_TIME_AbsoluteNBO)
640 - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose);
641 rid = get_op_id (h);
642 qe = GNUNET_new (struct GNUNET_NAMECACHE_QueueEntry);
643 qe->nsh = h;
644 qe->cont = cont;
645 qe->cont_cls = cont_cls;
646 qe->op_id = rid;
647 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe);
648
649 /* setup msg */
650 msg_size = sizeof (struct BlockCacheMessage) + blen;
651 pe = GNUNET_malloc (sizeof (struct PendingMessage) + msg_size);
652 pe->size = msg_size;
653 msg = (struct BlockCacheMessage *) &pe[1];
654 msg->gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_BLOCK_CACHE);
655 msg->gns_header.header.size = htons (msg_size);
656 msg->gns_header.r_id = htonl (rid);
657 msg->expire = block->expiration_time;
658 msg->signature = block->signature;
659 msg->derived_key = block->derived_key;
660 memcpy (&msg[1], &block[1], blen);
661 LOG (GNUNET_ERROR_TYPE_DEBUG,
662 "Sending `%s' message with size %u and expiration %s\n",
663 "NAMECACHE_BLOCK_CACHE",
664 (unsigned int) msg_size,
665 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (msg->expire)));
666 GNUNET_CONTAINER_DLL_insert_tail (h->pending_head, h->pending_tail, pe);
667 do_transmit (h);
668 return qe;
669}
670
671
672/**
673 * Get a result for a particular key from the namecache. The processor
674 * will only be called once.
675 *
676 * @param h handle to the namecache
677 * @param derived_hash hash of zone key combined with name to lookup
678 * @param proc function to call on the matching block, or with
679 * NULL if there is no matching block
680 * @param proc_cls closure for proc
681 * @return a handle that can be used to cancel
682 */
683struct GNUNET_NAMECACHE_QueueEntry *
684GNUNET_NAMECACHE_lookup_block (struct GNUNET_NAMECACHE_Handle *h,
685 const struct GNUNET_HashCode *derived_hash,
686 GNUNET_NAMESTORE_BlockProcessor proc, void *proc_cls)
687{
688 struct GNUNET_NAMECACHE_QueueEntry *qe;
689 struct PendingMessage *pe;
690 struct LookupBlockMessage *msg;
691 size_t msg_size;
692 uint32_t rid;
693
694 GNUNET_assert (NULL != h);
695 GNUNET_assert (NULL != derived_hash);
696 LOG (GNUNET_ERROR_TYPE_DEBUG,
697 "Looking for block under %s\n",
698 GNUNET_h2s (derived_hash));
699 rid = get_op_id(h);
700 qe = GNUNET_new (struct GNUNET_NAMECACHE_QueueEntry);
701 qe->nsh = h;
702 qe->block_proc = proc;
703 qe->block_proc_cls = proc_cls;
704 qe->op_id = rid;
705 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe);
706
707 msg_size = sizeof (struct LookupBlockMessage);
708 pe = GNUNET_malloc (sizeof (struct PendingMessage) + msg_size);
709 pe->size = msg_size;
710 msg = (struct LookupBlockMessage *) &pe[1];
711 msg->gns_header.header.type = htons (GNUNET_MESSAGE_TYPE_NAMECACHE_LOOKUP_BLOCK);
712 msg->gns_header.header.size = htons (msg_size);
713 msg->gns_header.r_id = htonl (rid);
714 msg->query = *derived_hash;
715 GNUNET_CONTAINER_DLL_insert_tail (h->pending_head, h->pending_tail, pe);
716 do_transmit (h);
717 return qe;
718}
719
720
721/**
722 * Cancel a namecache operation. The final callback from the
723 * operation must not have been done yet.
724 *
725 * @param qe operation to cancel
726 */
727void
728GNUNET_NAMECACHE_cancel (struct GNUNET_NAMECACHE_QueueEntry *qe)
729{
730 struct GNUNET_NAMECACHE_Handle *h = qe->nsh;
731
732 GNUNET_assert (NULL != qe);
733 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, qe);
734 GNUNET_free(qe);
735}
736
737
738/* end of namecache_api.c */
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c
new file mode 100644
index 000000000..72cfcf243
--- /dev/null
+++ b/src/namecache/plugin_namecache_postgres.c
@@ -0,0 +1,440 @@
1 /*
2 * This file is part of GNUnet
3 * (C) 2009-2013 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 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_postgres_lib.h"
31#include "namecache.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, "namecache-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, "namecache-postgres", __VA_ARGS__)
55
56
57/**
58 * Context for all functions in this plugin.
59 */
60struct 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 */
78static void
79create_indices (PGconn * dbh)
80{
81 /* create indices */
82 if ( (GNUNET_OK !=
83 GNUNET_POSTGRES_exec (dbh,
84 "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
85 (GNUNET_OK !=
86 GNUNET_POSTGRES_exec (dbh,
87 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) )
88 LOG (GNUNET_ERROR_TYPE_ERROR,
89 _("Failed to create indices\n"));
90}
91
92
93/**
94 * Initialize the database connections and associated
95 * data structures (create tables and indices
96 * as needed as well).
97 *
98 * @param plugin the plugin context (state for this module)
99 * @return GNUNET_OK on success
100 */
101static int
102database_setup (struct Plugin *plugin)
103{
104 PGresult *res;
105
106 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
107 "namecache-postgres");
108 if (NULL == plugin->dbh)
109 return GNUNET_SYSERR;
110 if (GNUNET_YES ==
111 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
112 "namecache-postgres",
113 "TEMPORARY_TABLE"))
114 {
115 res =
116 PQexec (plugin->dbh,
117 "CREATE TEMPORARY TABLE ns096blocks ("
118 " query BYTEA NOT NULL DEFAULT '',"
119 " block BYTEA NOT NULL DEFAULT '',"
120 " expiration_time BIGINT NOT NULL DEFAULT 0"
121 ")" "WITH OIDS");
122 }
123 else
124 {
125 res =
126 PQexec (plugin->dbh,
127 "CREATE TABLE ns096blocks ("
128 " query BYTEA NOT NULL DEFAULT '',"
129 " block BYTEA NOT NULL DEFAULT '',"
130 " expiration_time BIGINT NOT NULL DEFAULT 0"
131 ")" "WITH OIDS");
132 }
133 if ( (NULL == res) ||
134 ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
135 (0 != strcmp ("42P07", /* duplicate table */
136 PQresultErrorField
137 (res,
138 PG_DIAG_SQLSTATE)))))
139 {
140 (void) GNUNET_POSTGRES_check_result (plugin->dbh, res,
141 PGRES_COMMAND_OK, "CREATE TABLE",
142 "ns096blocks");
143 PQfinish (plugin->dbh);
144 plugin->dbh = NULL;
145 return GNUNET_SYSERR;
146 }
147 if (PQresultStatus (res) == PGRES_COMMAND_OK)
148 create_indices (plugin->dbh);
149 PQclear (res);
150
151 if ((GNUNET_OK !=
152 GNUNET_POSTGRES_prepare (plugin->dbh,
153 "cache_block",
154 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
155 "($1, $2, $3)", 3)) ||
156 (GNUNET_OK !=
157 GNUNET_POSTGRES_prepare (plugin->dbh,
158 "expire_blocks",
159 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
160 (GNUNET_OK !=
161 GNUNET_POSTGRES_prepare (plugin->dbh,
162 "delete_block",
163 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
164 (GNUNET_OK !=
165 GNUNET_POSTGRES_prepare (plugin->dbh,
166 "lookup_block",
167 "SELECT block FROM ns096blocks WHERE query=$1"
168 " ORDER BY expiration_time DESC LIMIT 1", 1)))
169 {
170 PQfinish (plugin->dbh);
171 plugin->dbh = NULL;
172 return GNUNET_SYSERR;
173 }
174 return GNUNET_OK;
175}
176
177
178/**
179 * Removes any expired block.
180 *
181 * @param plugin the plugin
182 */
183static void
184namecache_postgres_expire_blocks (struct Plugin *plugin)
185{
186 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
187 struct GNUNET_TIME_AbsoluteNBO now_be = GNUNET_TIME_absolute_hton (now);
188 const char *paramValues[] = {
189 (const char *) &now_be
190 };
191 int paramLengths[] = {
192 sizeof (now_be)
193 };
194 const int paramFormats[] = { 1 };
195 PGresult *res;
196
197 res =
198 PQexecPrepared (plugin->dbh, "expire_blocks", 1,
199 paramValues, paramLengths, paramFormats, 1);
200 if (GNUNET_OK !=
201 GNUNET_POSTGRES_check_result (plugin->dbh,
202 res,
203 PGRES_COMMAND_OK,
204 "PQexecPrepared",
205 "expire_blocks"))
206 return;
207 PQclear (res);
208}
209
210
211/**
212 * Delete older block in the datastore.
213 *
214 * @param the plugin
215 * @param query query for the block
216 * @param expiration time how old does the block have to be for deletion
217 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
218 */
219static void
220delete_old_block (struct Plugin *plugin,
221 const struct GNUNET_HashCode *query,
222 struct GNUNET_TIME_AbsoluteNBO expiration_time)
223{
224 const char *paramValues[] = {
225 (const char *) query,
226 (const char *) &expiration_time
227 };
228 int paramLengths[] = {
229 sizeof (*query),
230 sizeof (expiration_time)
231 };
232 const int paramFormats[] = { 1, 1 };
233 PGresult *res;
234
235 res =
236 PQexecPrepared (plugin->dbh, "delete_block", 2,
237 paramValues, paramLengths, paramFormats, 1);
238 if (GNUNET_OK !=
239 GNUNET_POSTGRES_check_result (plugin->dbh,
240 res,
241 PGRES_COMMAND_OK,
242 "PQexecPrepared",
243 "delete_block"))
244 return;
245 PQclear (res);
246}
247
248
249/**
250 * Cache a block in the datastore.
251 *
252 * @param cls closure (internal context for the plugin)
253 * @param block block to cache
254 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
255 */
256static int
257namecache_postgres_cache_block (void *cls,
258 const struct GNUNET_NAMESTORE_Block *block)
259{
260 struct Plugin *plugin = cls;
261 struct GNUNET_HashCode query;
262 size_t block_size = ntohl (block->purpose.size) +
263 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
264 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
265 const char *paramValues[] = {
266 (const char *) &query,
267 (const char *) block,
268 (const char *) &block->expiration_time
269 };
270 int paramLengths[] = {
271 sizeof (query),
272 (int) block_size,
273 sizeof (block->expiration_time)
274 };
275 const int paramFormats[] = { 1, 1, 1 };
276 PGresult *res;
277
278 namecache_postgres_expire_blocks (plugin);
279 GNUNET_CRYPTO_hash (&block->derived_key,
280 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
281 &query);
282 if (block_size > 64 * 65536)
283 {
284 GNUNET_break (0);
285 return GNUNET_SYSERR;
286 }
287 delete_old_block (plugin, &query, block->expiration_time);
288
289 res =
290 PQexecPrepared (plugin->dbh, "cache_block", 3,
291 paramValues, paramLengths, paramFormats, 1);
292 if (GNUNET_OK !=
293 GNUNET_POSTGRES_check_result (plugin->dbh,
294 res,
295 PGRES_COMMAND_OK,
296 "PQexecPrepared",
297 "cache_block"))
298 return GNUNET_SYSERR;
299 PQclear (res);
300 return GNUNET_OK;
301}
302
303
304/**
305 * Get the block for a particular zone and label in the
306 * datastore. Will return at most one result to the iterator.
307 *
308 * @param cls closure (internal context for the plugin)
309 * @param query hash of public key derived from the zone and the label
310 * @param iter function to call with the result
311 * @param iter_cls closure for @a iter
312 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
313 */
314static int
315namecache_postgres_lookup_block (void *cls,
316 const struct GNUNET_HashCode *query,
317 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
318{
319 struct Plugin *plugin = cls;
320 const char *paramValues[] = {
321 (const char *) query
322 };
323 int paramLengths[] = {
324 sizeof (*query)
325 };
326 const int paramFormats[] = { 1 };
327 PGresult *res;
328 unsigned int cnt;
329 size_t bsize;
330 const struct GNUNET_NAMESTORE_Block *block;
331
332 res = PQexecPrepared (plugin->dbh,
333 "lookup_block", 1,
334 paramValues, paramLengths, paramFormats,
335 1);
336 if (GNUNET_OK !=
337 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
338 "PQexecPrepared",
339 "lookup_block"))
340 {
341 LOG (GNUNET_ERROR_TYPE_DEBUG,
342 "Failing lookup (postgres error)\n");
343 return GNUNET_SYSERR;
344 }
345 if (0 == (cnt = PQntuples (res)))
346 {
347 /* no result */
348 LOG (GNUNET_ERROR_TYPE_DEBUG,
349 "Ending iteration (no more results)\n");
350 PQclear (res);
351 return GNUNET_NO;
352 }
353 GNUNET_assert (1 == cnt);
354 GNUNET_assert (1 != PQnfields (res));
355 bsize = PQgetlength (res, 0, 0);
356 block = (const struct GNUNET_NAMESTORE_Block *) PQgetvalue (res, 0, 0);
357 if ( (bsize < sizeof (*block)) ||
358 (bsize != ntohl (block->purpose.size) +
359 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
360 sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) )
361 {
362 GNUNET_break (0);
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "Failing lookup (corrupt block)\n");
365 PQclear (res);
366 return GNUNET_SYSERR;
367 }
368 iter (iter_cls, block);
369 PQclear (res);
370 return GNUNET_OK;
371}
372
373
374/**
375 * Shutdown database connection and associate data
376 * structures.
377 *
378 * @param plugin the plugin context (state for this module)
379 */
380static void
381database_shutdown (struct Plugin *plugin)
382{
383 PQfinish (plugin->dbh);
384 plugin->dbh = NULL;
385}
386
387
388/**
389 * Entry point for the plugin.
390 *
391 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
392 * @return NULL on error, othrewise the plugin context
393 */
394void *
395libgnunet_plugin_namecache_postgres_init (void *cls)
396{
397 static struct Plugin plugin;
398 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
399 struct GNUNET_NAMECACHE_PluginFunctions *api;
400
401 if (NULL != plugin.cfg)
402 return NULL; /* can only initialize once! */
403 memset (&plugin, 0, sizeof (struct Plugin));
404 plugin.cfg = cfg;
405 if (GNUNET_OK != database_setup (&plugin))
406 {
407 database_shutdown (&plugin);
408 return NULL;
409 }
410 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
411 api->cls = &plugin;
412 api->cache_block = &namecache_postgres_cache_block;
413 api->lookup_block = &namecache_postgres_lookup_block;
414 LOG (GNUNET_ERROR_TYPE_INFO,
415 _("Postgres database running\n"));
416 return api;
417}
418
419
420/**
421 * Exit point from the plugin.
422 *
423 * @param cls the plugin context (as returned by "init")
424 * @return always NULL
425 */
426void *
427libgnunet_plugin_namecache_postgres_done (void *cls)
428{
429 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
430 struct Plugin *plugin = api->cls;
431
432 database_shutdown (plugin);
433 plugin->cfg = NULL;
434 GNUNET_free (api);
435 LOG (GNUNET_ERROR_TYPE_DEBUG,
436 "postgres plugin is finished\n");
437 return NULL;
438}
439
440/* end of plugin_namecache_postgres.c */
diff --git a/src/namecache/plugin_namecache_sqlite.c b/src/namecache/plugin_namecache_sqlite.c
new file mode 100644
index 000000000..7eb8b4503
--- /dev/null
+++ b/src/namecache/plugin_namecache_sqlite.c
@@ -0,0 +1,608 @@
1 /*
2 * This file is part of GNUnet
3 * (C) 2009-2013 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 namecache/plugin_namecache_sqlite.c
23 * @brief sqlite-based namecache backend
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_namecache_plugin.h"
29#include "gnunet_namecache_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "namecache.h"
32#include <sqlite3.h>
33
34/**
35 * After how many ms "busy" should a DB operation fail for good? A
36 * low value makes sure that we are more responsive to requests
37 * (especially PUTs). A high value guarantees a higher success rate
38 * (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
42 * succeed 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_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "namecache-sqlite", _("`%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, "namecache-sqlite", __VA_ARGS__)
55
56
57/**
58 * Context for all functions in this plugin.
59 */
60struct Plugin
61{
62
63 const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65 /**
66 * Database filename.
67 */
68 char *fn;
69
70 /**
71 * Native SQLite database handle.
72 */
73 sqlite3 *dbh;
74
75 /**
76 * Precompiled SQL for caching a block
77 */
78 sqlite3_stmt *cache_block;
79
80 /**
81 * Precompiled SQL for deleting an older block
82 */
83 sqlite3_stmt *delete_block;
84
85 /**
86 * Precompiled SQL for looking up a block
87 */
88 sqlite3_stmt *lookup_block;
89
90 /**
91 * Precompiled SQL for removing expired blocks
92 */
93 sqlite3_stmt *expire_blocks;
94
95
96};
97
98
99/**
100 * @brief Prepare a SQL statement
101 *
102 * @param dbh handle to the database
103 * @param zSql SQL statement, UTF-8 encoded
104 * @param ppStmt set to the prepared statement
105 * @return 0 on success
106 */
107static int
108sq_prepare (sqlite3 * dbh, const char *zSql, sqlite3_stmt ** ppStmt)
109{
110 char *dummy;
111 int result;
112
113 result =
114 sqlite3_prepare_v2 (dbh, zSql, strlen (zSql), ppStmt,
115 (const char **) &dummy);
116 LOG (GNUNET_ERROR_TYPE_DEBUG,
117 "Prepared `%s' / %p: %d\n", zSql, *ppStmt, result);
118 return result;
119}
120
121
122/**
123 * Create our database indices.
124 *
125 * @param dbh handle to the database
126 */
127static void
128create_indices (sqlite3 * dbh)
129{
130 /* create indices */
131 if ( (SQLITE_OK !=
132 sqlite3_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_query_hash ON ns096blocks (query,expiration_time)",
133 NULL, NULL, NULL)) ||
134 (SQLITE_OK !=
135 sqlite3_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_block_expiration ON ns096blocks (expiration_time)",
136 NULL, NULL, NULL)) )
137 LOG (GNUNET_ERROR_TYPE_ERROR,
138 "Failed to create indices: %s\n", sqlite3_errmsg (dbh));
139}
140
141
142#if 0
143#define CHECK(a) GNUNET_break(a)
144#define ENULL NULL
145#else
146#define ENULL &e
147#define ENULL_DEFINED 1
148#define CHECK(a) if (! a) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
149#endif
150
151
152/**
153 * Initialize the database connections and associated
154 * data structures (create tables and indices
155 * as needed as well).
156 *
157 * @param plugin the plugin context (state for this module)
158 * @return #GNUNET_OK on success
159 */
160static int
161database_setup (struct Plugin *plugin)
162{
163 sqlite3_stmt *stmt;
164 char *afsdir;
165#if ENULL_DEFINED
166 char *e;
167#endif
168
169 if (GNUNET_OK !=
170 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "namecache-sqlite",
171 "FILENAME", &afsdir))
172 {
173 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
174 "namecache-sqlite", "FILENAME");
175 return GNUNET_SYSERR;
176 }
177 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
178 {
179 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
180 {
181 GNUNET_break (0);
182 GNUNET_free (afsdir);
183 return GNUNET_SYSERR;
184 }
185 }
186 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
187 plugin->fn = afsdir;
188
189 /* Open database and precompile statements */
190 if (sqlite3_open (plugin->fn, &plugin->dbh) != SQLITE_OK)
191 {
192 LOG (GNUNET_ERROR_TYPE_ERROR,
193 _("Unable to initialize SQLite: %s.\n"),
194 sqlite3_errmsg (plugin->dbh));
195 return GNUNET_SYSERR;
196 }
197 CHECK (SQLITE_OK ==
198 sqlite3_exec (plugin->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL,
199 ENULL));
200 CHECK (SQLITE_OK ==
201 sqlite3_exec (plugin->dbh, "PRAGMA synchronous=NORMAL", NULL, NULL,
202 ENULL));
203 CHECK (SQLITE_OK ==
204 sqlite3_exec (plugin->dbh, "PRAGMA legacy_file_format=OFF", NULL, NULL,
205 ENULL));
206 CHECK (SQLITE_OK ==
207 sqlite3_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL", NULL,
208 NULL, ENULL));
209 CHECK (SQLITE_OK ==
210 sqlite3_exec (plugin->dbh, "PRAGMA encoding=\"UTF-8\"", NULL,
211 NULL, ENULL));
212 CHECK (SQLITE_OK ==
213 sqlite3_exec (plugin->dbh, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
214 ENULL));
215 CHECK (SQLITE_OK ==
216 sqlite3_exec (plugin->dbh, "PRAGMA count_changes=OFF", NULL, NULL,
217 ENULL));
218 CHECK (SQLITE_OK ==
219 sqlite3_exec (plugin->dbh, "PRAGMA page_size=4092", NULL, NULL,
220 ENULL));
221
222 CHECK (SQLITE_OK == sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));
223
224
225 /* Create tables */
226 CHECK (SQLITE_OK ==
227 sq_prepare (plugin->dbh,
228 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'ns096blocks'",
229 &stmt));
230 if ((sqlite3_step (stmt) == SQLITE_DONE) &&
231 (sqlite3_exec
232 (plugin->dbh,
233 "CREATE TABLE ns096blocks ("
234 " query BLOB NOT NULL DEFAULT '',"
235 " block BLOB NOT NULL DEFAULT '',"
236 " expiration_time INT8 NOT NULL DEFAULT 0"
237 ")",
238 NULL, NULL, NULL) != SQLITE_OK))
239 {
240 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec");
241 sqlite3_finalize (stmt);
242 return GNUNET_SYSERR;
243 }
244 sqlite3_finalize (stmt);
245 create_indices (plugin->dbh);
246
247 if ((sq_prepare
248 (plugin->dbh,
249 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
250 &plugin->cache_block) != SQLITE_OK) ||
251 (sq_prepare
252 (plugin->dbh,
253 "DELETE FROM ns096blocks WHERE expiration_time<?",
254 &plugin->expire_blocks) != SQLITE_OK) ||
255 (sq_prepare
256 (plugin->dbh,
257 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
258 &plugin->delete_block) != SQLITE_OK) ||
259 (sq_prepare
260 (plugin->dbh,
261 "SELECT block FROM ns096blocks WHERE query=? ORDER BY expiration_time DESC LIMIT 1",
262 &plugin->lookup_block) != SQLITE_OK)
263 )
264 {
265 LOG_SQLITE (plugin,GNUNET_ERROR_TYPE_ERROR, "precompiling");
266 return GNUNET_SYSERR;
267 }
268 return GNUNET_OK;
269}
270
271
272/**
273 * Shutdown database connection and associate data
274 * structures.
275 * @param plugin the plugin context (state for this module)
276 */
277static void
278database_shutdown (struct Plugin *plugin)
279{
280 int result;
281 sqlite3_stmt *stmt;
282
283 if (NULL != plugin->cache_block)
284 sqlite3_finalize (plugin->cache_block);
285 if (NULL != plugin->lookup_block)
286 sqlite3_finalize (plugin->lookup_block);
287 if (NULL != plugin->expire_blocks)
288 sqlite3_finalize (plugin->expire_blocks);
289 if (NULL != plugin->delete_block)
290 sqlite3_finalize (plugin->delete_block);
291 result = sqlite3_close (plugin->dbh);
292 if (result == SQLITE_BUSY)
293 {
294 LOG (GNUNET_ERROR_TYPE_WARNING,
295 _("Tried to close sqlite without finalizing all prepared statements.\n"));
296 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
297 while (stmt != NULL)
298 {
299 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
300 "Closing statement %p\n", stmt);
301 result = sqlite3_finalize (stmt);
302 if (result != SQLITE_OK)
303 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite",
304 "Failed to close statement %p: %d\n", stmt, result);
305 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
306 }
307 result = sqlite3_close (plugin->dbh);
308 }
309 if (SQLITE_OK != result)
310 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
311
312 GNUNET_free_non_null (plugin->fn);
313}
314
315
316/**
317 * Removes any expired block.
318 *
319 * @param plugin the plugin
320 */
321static void
322namecache_sqlite_expire_blocks (struct Plugin *plugin)
323{
324 struct GNUNET_TIME_Absolute now;
325 int n;
326
327 now = GNUNET_TIME_absolute_get ();
328 if (SQLITE_OK != sqlite3_bind_int64 (plugin->expire_blocks,
329 1, now.abs_value_us))
330 {
331 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
332 "sqlite3_bind_XXXX");
333 if (SQLITE_OK != sqlite3_reset (plugin->expire_blocks))
334 LOG_SQLITE (plugin,
335 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
336 "sqlite3_reset");
337 return;
338 }
339 n = sqlite3_step (plugin->expire_blocks);
340 if (SQLITE_OK != sqlite3_reset (plugin->expire_blocks))
341 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
342 "sqlite3_reset");
343 switch (n)
344 {
345 case SQLITE_DONE:
346 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", "Records expired\n");
347 return;
348 case SQLITE_BUSY:
349 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
350 "sqlite3_step");
351 return;
352 default:
353 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
354 "sqlite3_step");
355 return;
356 }
357}
358
359
360/**
361 * Cache a block in the datastore.
362 *
363 * @param cls closure (internal context for the plugin)
364 * @param block block to cache
365 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
366 */
367static int
368namecache_sqlite_cache_block (void *cls,
369 const struct GNUNET_NAMESTORE_Block *block)
370{
371 struct Plugin *plugin = cls;
372 struct GNUNET_HashCode query;
373 struct GNUNET_TIME_Absolute expiration;
374 int64_t dval;
375 size_t block_size;
376 int n;
377
378 namecache_sqlite_expire_blocks (plugin);
379 GNUNET_CRYPTO_hash (&block->derived_key,
380 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
381 &query);
382 expiration = GNUNET_TIME_absolute_ntoh (block->expiration_time);
383 dval = (int64_t) expiration.abs_value_us;
384 if (dval < 0)
385 dval = INT64_MAX;
386 block_size = ntohl (block->purpose.size) +
387 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
388 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
389 if (block_size > 64 * 65536)
390 {
391 GNUNET_break (0);
392 return GNUNET_SYSERR;
393 }
394
395 /* delete old version of the block */
396 if ( (SQLITE_OK !=
397 sqlite3_bind_blob (plugin->delete_block, 1,
398 &query, sizeof (struct GNUNET_HashCode),
399 SQLITE_STATIC)) ||
400 (SQLITE_OK !=
401 sqlite3_bind_int64 (plugin->delete_block,
402 2, dval)) )
403 {
404 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
405 "sqlite3_bind_XXXX");
406 if (SQLITE_OK != sqlite3_reset (plugin->delete_block))
407 LOG_SQLITE (plugin,
408 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
409 "sqlite3_reset");
410 return GNUNET_SYSERR;
411 }
412 n = sqlite3_step (plugin->delete_block);
413 switch (n)
414 {
415 case SQLITE_DONE:
416 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", "Old block deleted\n");
417 break;
418 case SQLITE_BUSY:
419 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
420 "sqlite3_step");
421 break;
422 default:
423 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
424 "sqlite3_step");
425 break;
426 }
427 if (SQLITE_OK != sqlite3_reset (plugin->delete_block))
428 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
429 "sqlite3_reset");
430
431 /* insert new version of the block */
432 if ((SQLITE_OK !=
433 sqlite3_bind_blob (plugin->cache_block, 1,
434 &query, sizeof (struct GNUNET_HashCode),
435 SQLITE_STATIC)) ||
436 (SQLITE_OK !=
437 sqlite3_bind_blob (plugin->cache_block, 2,
438 block, block_size,
439 SQLITE_STATIC)) ||
440 (SQLITE_OK !=
441 sqlite3_bind_int64 (plugin->cache_block, 3,
442 dval)))
443 {
444 LOG_SQLITE (plugin,
445 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
446 "sqlite3_bind_XXXX");
447 if (SQLITE_OK != sqlite3_reset (plugin->cache_block))
448 LOG_SQLITE (plugin,
449 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
450 "sqlite3_reset");
451 return GNUNET_SYSERR;
452
453 }
454 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455 "Caching block under derived key `%s'\n",
456 GNUNET_h2s_full (&query));
457 n = sqlite3_step (plugin->cache_block);
458 if (SQLITE_OK != sqlite3_reset (plugin->cache_block))
459 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
460 "sqlite3_reset");
461 switch (n)
462 {
463 case SQLITE_DONE:
464 LOG (GNUNET_ERROR_TYPE_DEBUG,
465 "Record stored\n");
466 return GNUNET_OK;
467 case SQLITE_BUSY:
468 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
469 "sqlite3_step");
470 return GNUNET_NO;
471 default:
472 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
473 "sqlite3_step");
474 return GNUNET_SYSERR;
475 }
476}
477
478
479/**
480 * Get the block for a particular zone and label in the
481 * datastore. Will return at most one result to the iterator.
482 *
483 * @param cls closure (internal context for the plugin)
484 * @param query hash of public key derived from the zone and the label
485 * @param iter function to call with the result
486 * @param iter_cls closure for @a iter
487 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
488 */
489static int
490namecache_sqlite_lookup_block (void *cls,
491 const struct GNUNET_HashCode *query,
492 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
493{
494 struct Plugin *plugin = cls;
495 int ret;
496 int sret;
497 size_t block_size;
498 const struct GNUNET_NAMESTORE_Block *block;
499
500 if (SQLITE_OK != sqlite3_bind_blob (plugin->lookup_block, 1,
501 query, sizeof (struct GNUNET_HashCode),
502 SQLITE_STATIC))
503 {
504 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
505 "sqlite3_bind_XXXX");
506 if (SQLITE_OK != sqlite3_reset (plugin->lookup_block))
507 LOG_SQLITE (plugin,
508 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
509 "sqlite3_reset");
510 return GNUNET_SYSERR;
511 }
512 ret = GNUNET_NO;
513 if (SQLITE_ROW == (sret = sqlite3_step (plugin->lookup_block)))
514 {
515 block = sqlite3_column_blob (plugin->lookup_block, 0);
516 block_size = sqlite3_column_bytes (plugin->lookup_block, 0);
517 if ( (block_size < sizeof (struct GNUNET_NAMESTORE_Block)) ||
518 (ntohl (block->purpose.size) +
519 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
520 sizeof (struct GNUNET_CRYPTO_EcdsaSignature) != block_size) )
521 {
522 GNUNET_break (0);
523 ret = GNUNET_SYSERR;
524 }
525 else
526 {
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Found block under derived key `%s'\n",
529 GNUNET_h2s_full (query));
530 iter (iter_cls, block);
531 ret = GNUNET_YES;
532 }
533 }
534 else
535 {
536 if (SQLITE_DONE != sret)
537 {
538 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_step");
539 ret = GNUNET_SYSERR;
540 }
541 else
542 {
543 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544 "No block found under derived key `%s'\n",
545 GNUNET_h2s_full (query));
546 }
547 }
548 if (SQLITE_OK != sqlite3_reset (plugin->lookup_block))
549 LOG_SQLITE (plugin,
550 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
551 "sqlite3_reset");
552 return ret;
553}
554
555
556/**
557 * Entry point for the plugin.
558 *
559 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
560 * @return NULL on error, otherwise the plugin context
561 */
562void *
563libgnunet_plugin_namecache_sqlite_init (void *cls)
564{
565 static struct Plugin plugin;
566 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
567 struct GNUNET_NAMECACHE_PluginFunctions *api;
568
569 if (NULL != plugin.cfg)
570 return NULL; /* can only initialize once! */
571 memset (&plugin, 0, sizeof (struct Plugin));
572 plugin.cfg = cfg;
573 if (GNUNET_OK != database_setup (&plugin))
574 {
575 database_shutdown (&plugin);
576 return NULL;
577 }
578 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
579 api->cls = &plugin;
580 api->cache_block = &namecache_sqlite_cache_block;
581 api->lookup_block = &namecache_sqlite_lookup_block;
582 LOG (GNUNET_ERROR_TYPE_INFO,
583 _("Sqlite database running\n"));
584 return api;
585}
586
587
588/**
589 * Exit point from the plugin.
590 *
591 * @param cls the plugin context (as returned by "init")
592 * @return always NULL
593 */
594void *
595libgnunet_plugin_namecache_sqlite_done (void *cls)
596{
597 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
598 struct Plugin *plugin = api->cls;
599
600 database_shutdown (plugin);
601 plugin->cfg = NULL;
602 GNUNET_free (api);
603 LOG (GNUNET_ERROR_TYPE_DEBUG,
604 "sqlite plugin is finished\n");
605 return NULL;
606}
607
608/* end of plugin_namecache_sqlite.c */
diff --git a/src/namecache/test_namecache_api.conf b/src/namecache/test_namecache_api.conf
new file mode 100644
index 000000000..908b0e294
--- /dev/null
+++ b/src/namecache/test_namecache_api.conf
@@ -0,0 +1,19 @@
1[PATHS]
2GNUNET_TEST_HOME = /tmp/test-gnunet-namecache/
3
4[arm]
5PORT = 12000
6DEFAULTSERVICES = namecache
7
8[namecache]
9#PREFIX = valgrind
10AUTOSTART = YES
11DATABASE = sqlite
12
13[namecache-sqlite]
14FILENAME = $GNUNET_TEST_HOME/namecache/sqlite_test.db
15
16[namecache-postgres]
17CONFIG = connect_timeout=10; dbname=gnunetcheck
18TEMPORARY_TABLE = YES
19
diff --git a/src/namecache/test_namecache_api_cache_block.c b/src/namecache/test_namecache_api_cache_block.c
new file mode 100644
index 000000000..f28016cd1
--- /dev/null
+++ b/src/namecache/test_namecache_api_cache_block.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 (C) 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 * @file namecache/test_namecache_api.c
22 * @brief testcase for namecache_api.c: store a record and perform a lookup
23 */
24#include "platform.h"
25#include "gnunet_namecache_service.h"
26#include "gnunet_testing_lib.h"
27
28#define TEST_RECORD_TYPE 1234
29
30#define TEST_RECORD_DATALEN 123
31
32#define TEST_RECORD_DATA 'a'
33
34#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100)
35
36
37static struct GNUNET_NAMECACHE_Handle *nsh;
38
39static GNUNET_SCHEDULER_TaskIdentifier endbadly_task;
40
41static struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
42
43static struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
44
45static int res;
46
47static struct GNUNET_NAMECACHE_QueueEntry *nsqe;
48
49
50static void
51cleanup ()
52{
53 if (NULL != nsh)
54 {
55 GNUNET_NAMECACHE_disconnect (nsh);
56 nsh = NULL;
57 }
58 if (NULL != privkey)
59 {
60 GNUNET_free (privkey);
61 privkey = NULL;
62 }
63 GNUNET_SCHEDULER_shutdown ();
64}
65
66
67/**
68 * Re-establish the connection to the service.
69 *
70 * @param cls handle to use to re-connect.
71 * @param tc scheduler context
72 */
73static void
74endbadly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
75{
76 if (NULL != nsqe)
77 {
78 GNUNET_NAMECACHE_cancel (nsqe);
79 nsqe = NULL;
80 }
81 cleanup ();
82 res = 1;
83}
84
85
86static void
87end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
88{
89 cleanup ();
90 res = 0;
91}
92
93
94static void
95rd_decrypt_cb (void *cls,
96 unsigned int rd_count,
97 const struct GNUNET_NAMESTORE_RecordData *rd)
98{
99 char rd_cmp_data[TEST_RECORD_DATALEN];
100
101 GNUNET_assert (1 == rd_count);
102 GNUNET_assert (NULL != rd);
103
104 memset (rd_cmp_data, 'a', TEST_RECORD_DATALEN);
105
106 GNUNET_assert (TEST_RECORD_TYPE == rd[0].record_type);
107 GNUNET_assert (TEST_RECORD_DATALEN == rd[0].data_size);
108 GNUNET_assert (0 == memcmp (&rd_cmp_data, rd[0].data, TEST_RECORD_DATALEN));
109
110 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
111 "Block was decrypted successfully \n");
112
113 GNUNET_SCHEDULER_add_now (&end, NULL);
114}
115
116static void
117name_lookup_proc (void *cls,
118 const struct GNUNET_NAMESTORE_Block *block)
119{
120 const char *name = cls;
121 nsqe = NULL;
122
123 GNUNET_assert (NULL != cls);
124
125 if (endbadly_task != GNUNET_SCHEDULER_NO_TASK)
126 {
127 GNUNET_SCHEDULER_cancel (endbadly_task);
128 endbadly_task = GNUNET_SCHEDULER_NO_TASK;
129 }
130
131 if (NULL == block)
132 {
133 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
134 _("Namecache returned no block\n"));
135 if (endbadly_task != GNUNET_SCHEDULER_NO_TASK)
136 GNUNET_SCHEDULER_cancel (endbadly_task);
137 endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL);
138 return;
139 }
140
141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142 "Namecache returned block, decrypting \n");
143 GNUNET_assert (GNUNET_OK == GNUNET_NAMESTORE_block_decrypt(block,
144 &pubkey, name, &rd_decrypt_cb, (void *) name));
145}
146
147static void
148cache_cont (void *cls, int32_t success, const char *emsg)
149{
150 const char *name = cls;
151 struct GNUNET_HashCode derived_hash;
152
153 GNUNET_assert (NULL != cls);
154
155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
156 "Name store cached record for `%s': %s\n",
157 name,
158 (success == GNUNET_OK) ? "SUCCESS" : "FAIL");
159
160 /* Create derived hash */
161 GNUNET_NAMESTORE_query_from_public_key (&pubkey, name, &derived_hash);
162
163 nsqe = GNUNET_NAMECACHE_lookup_block (nsh, &derived_hash,
164 &name_lookup_proc, (void *) name);
165}
166
167
168static void
169run (void *cls,
170 const struct GNUNET_CONFIGURATION_Handle *cfg,
171 struct GNUNET_TESTING_Peer *peer)
172{
173 struct GNUNET_NAMESTORE_RecordData rd;
174 struct GNUNET_NAMESTORE_Block *block;
175 char *hostkey_file;
176 const char * name = "dummy.dummy.gnunet";
177
178 endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
179 &endbadly, NULL);
180 GNUNET_asprintf (&hostkey_file,
181 "zonefiles%s%s",
182 DIR_SEPARATOR_STR,
183 "N0UJMP015AFUNR2BTNM3FKPBLG38913BL8IDMCO2H0A1LIB81960.zkey");
184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using zonekey file `%s' \n", hostkey_file);
185 privkey = GNUNET_CRYPTO_ecdsa_key_create_from_file (hostkey_file);
186 GNUNET_free (hostkey_file);
187 GNUNET_assert (privkey != NULL);
188 GNUNET_CRYPTO_ecdsa_key_get_public (privkey, &pubkey);
189
190
191 rd.expiration_time = GNUNET_TIME_absolute_get().abs_value_us;
192 rd.record_type = TEST_RECORD_TYPE;
193 rd.data_size = TEST_RECORD_DATALEN;
194 rd.data = GNUNET_malloc (TEST_RECORD_DATALEN);
195 memset ((char *) rd.data, 'a', TEST_RECORD_DATALEN);
196 block = GNUNET_NAMESTORE_block_create (privkey,
197 GNUNET_TIME_UNIT_FOREVER_ABS, name, &rd, 1 );
198 if (NULL == block)
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 _("Namecache cannot cache no block\n"));
202 }
203
204 nsh = GNUNET_NAMECACHE_connect (cfg);
205 if (NULL == nsh)
206 {
207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208 _("Namecache cannot connect to namecache\n"));
209 }
210 GNUNET_break (NULL != nsh);
211
212 nsqe = GNUNET_NAMECACHE_block_cache (nsh, block , &cache_cont, (void *) name);
213 if (NULL == nsqe)
214 {
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216 _("Namecache cannot cache no block\n"));
217 }
218
219 GNUNET_free ((void *)rd.data);
220}
221
222
223int
224main (int argc, char *argv[])
225{
226 GNUNET_DISK_directory_remove ("/tmp/test-gnunet-namecache/");
227 res = 1;
228 if (0 !=
229 GNUNET_TESTING_service_run ("test-namecache-api",
230 "namecache",
231 "test_namecache_api.conf",
232 &run,
233 NULL))
234 return 1;
235 return res;
236}
237
238
239/* end of test_namecache_api_cache_block.c */
diff --git a/src/namecache/test_plugin_namecache.c b/src/namecache/test_plugin_namecache.c
new file mode 100644
index 000000000..38b004aa5
--- /dev/null
+++ b/src/namecache/test_plugin_namecache.c
@@ -0,0 +1,131 @@
1/*
2 This file is part of GNUnet.
3 (C) 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 * @file namecache/test_plugin_namecache.c
22 * @brief Test for the namecache plugins
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_namecache_plugin.h"
28#include "gnunet_testing_lib.h"
29
30
31static int ok;
32
33/**
34 * Name of plugin under test.
35 */
36static const char *plugin_name;
37
38
39/**
40 * Function called when the service shuts down. Unloads our namecache
41 * plugin.
42 *
43 * @param api api to unload
44 */
45static void
46unload_plugin (struct GNUNET_NAMECACHE_PluginFunctions *api)
47{
48 char *libname;
49
50 GNUNET_asprintf (&libname, "libgnunet_plugin_namecache_%s", plugin_name);
51 GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
52 GNUNET_free (libname);
53}
54
55
56/**
57 * Load the namecache plugin.
58 *
59 * @param cfg configuration to pass
60 * @return NULL on error
61 */
62static struct GNUNET_NAMECACHE_PluginFunctions *
63load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
64{
65 struct GNUNET_NAMECACHE_PluginFunctions *ret;
66 char *libname;
67
68 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' namecache plugin\n"),
69 plugin_name);
70 GNUNET_asprintf (&libname, "libgnunet_plugin_namecache_%s", plugin_name);
71 if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void*) cfg)))
72 {
73 FPRINTF (stderr, "Failed to load plugin `%s'!\n", plugin_name);
74 GNUNET_free (libname);
75 return NULL;
76 }
77 GNUNET_free (libname);
78 return ret;
79}
80
81
82static void
83run (void *cls, char *const *args, const char *cfgfile,
84 const struct GNUNET_CONFIGURATION_Handle *cfg)
85{
86 struct GNUNET_NAMECACHE_PluginFunctions *nsp;
87
88 ok = 0;
89 nsp = load_plugin (cfg);
90 if (NULL == nsp)
91 {
92 FPRINTF (stderr,
93 "%s",
94 "Failed to initialize namecache. Database likely not setup, skipping test.\n");
95 return;
96 }
97
98 unload_plugin (nsp);
99}
100
101
102int
103main (int argc, char *argv[])
104{
105 char cfg_name[128];
106 char *const xargv[] = {
107 "test-plugin-namecache",
108 "-c",
109 cfg_name,
110 NULL
111 };
112 struct GNUNET_GETOPT_CommandLineOption options[] = {
113 GNUNET_GETOPT_OPTION_END
114 };
115
116 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-namecache-sqlite");
117 GNUNET_log_setup ("test-plugin-namecache",
118 "WARNING",
119 NULL);
120 plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
121 GNUNET_snprintf (cfg_name, sizeof (cfg_name), "test_plugin_namecache_%s.conf",
122 plugin_name);
123 GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
124 "test-plugin-namecache", "nohelp", options, &run, NULL);
125 if (ok != 0)
126 FPRINTF (stderr, "Missed some testcases: %d\n", ok);
127 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-namecache-sqlite");
128 return ok;
129}
130
131/* end of test_plugin_namecache.c */
diff --git a/src/namecache/test_plugin_namecache_postgres.conf b/src/namecache/test_plugin_namecache_postgres.conf
new file mode 100644
index 000000000..8473857d5
--- /dev/null
+++ b/src/namecache/test_plugin_namecache_postgres.conf
@@ -0,0 +1,3 @@
1[namestore-postgres]
2CONFIG = connect_timeout=10; dbname=gnunetcheck
3TEMPORARY_TABLE = YES
diff --git a/src/namecache/test_plugin_namecache_sqlite.conf b/src/namecache/test_plugin_namecache_sqlite.conf
new file mode 100644
index 000000000..57b170f7a
--- /dev/null
+++ b/src/namecache/test_plugin_namecache_sqlite.conf
@@ -0,0 +1,2 @@
1[namestore-sqlite]
2FILENAME = /tmp/gnunet-test-plugin-namestore-sqlite/sqlite.db