aboutsummaryrefslogtreecommitdiff
path: root/src/cli/namestore
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/namestore')
-rw-r--r--src/cli/namestore/.gitignore6
-rw-r--r--src/cli/namestore/Makefile.am70
-rw-r--r--src/cli/namestore/example_zonefile27
-rw-r--r--src/cli/namestore/gnunet-namestore-dbtool.c199
-rw-r--r--src/cli/namestore/gnunet-namestore-zonefile.c729
-rw-r--r--src/cli/namestore/gnunet-namestore.c2118
-rw-r--r--src/cli/namestore/gnunet-zoneimport.c1882
-rw-r--r--src/cli/namestore/meson.build38
-rwxr-xr-xsrc/cli/namestore/test_namestore_box_lightest.sh63
-rwxr-xr-xsrc/cli/namestore/test_namestore_delete.sh68
-rwxr-xr-xsrc/cli/namestore/test_namestore_lookup.sh63
-rwxr-xr-xsrc/cli/namestore/test_namestore_put.sh55
-rwxr-xr-xsrc/cli/namestore/test_namestore_put_multiple.sh112
-rwxr-xr-xsrc/cli/namestore/test_namestore_put_stdin.sh68
-rwxr-xr-xsrc/cli/namestore/test_namestore_zonefile_import.sh33
15 files changed, 5531 insertions, 0 deletions
diff --git a/src/cli/namestore/.gitignore b/src/cli/namestore/.gitignore
new file mode 100644
index 000000000..3c0282e8c
--- /dev/null
+++ b/src/cli/namestore/.gitignore
@@ -0,0 +1,6 @@
1gnunet-namestore
2gnunet-namestore-dbtool
3gnunet-namestore-zonefile
4gnunet-namestore-fcfsd
5gnunet-zoneimport
6
diff --git a/src/cli/namestore/Makefile.am b/src/cli/namestore/Makefile.am
new file mode 100644
index 000000000..b666c8851
--- /dev/null
+++ b/src/cli/namestore/Makefile.am
@@ -0,0 +1,70 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10sqldir = $(prefix)/share/gnunet/sql/
11
12if USE_COVERAGE
13 AM_CFLAGS = --coverage -O0
14 XLIBS = -lgcov
15endif
16
17
18bin_PROGRAMS = \
19 gnunet-namestore \
20 gnunet-namestore-dbtool \
21 gnunet-namestore-zonefile \
22 gnunet-zoneimport
23
24gnunet_namestore_zonefile_SOURCES = \
25 gnunet-namestore-zonefile.c
26gnunet_namestore_zonefile_LDADD = \
27 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
28 $(top_builddir)/src/service/identity/libgnunetidentity.la \
29 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \
31 $(GN_LIBINTL)
32
33gnunet_zoneimport_SOURCES = \
34 gnunet-zoneimport.c
35gnunet_zoneimport_LDADD = \
36 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
37 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
38 $(top_builddir)/src/service/identity/libgnunetidentity.la \
39 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
40 $(top_builddir)/src/lib/util/libgnunetutil.la \
41 $(GN_LIBINTL)
42
43gnunet_namestore_SOURCES = \
44 gnunet-namestore.c
45gnunet_namestore_LDADD = \
46 $(top_builddir)/src/service/identity/libgnunetidentity.la \
47 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
48 $(top_builddir)/src/lib/util/libgnunetutil.la \
49 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
50 $(GN_LIBINTL)
51
52gnunet_namestore_dbtool_SOURCES = \
53 gnunet-namestore-dbtool.c
54gnunet_namestore_dbtool_LDADD = \
55 $(top_builddir)/src/lib/util/libgnunetutil.la \
56 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
57 $(GN_LIBINTL)
58
59
60
61check_SCRIPTS = \
62 test_namestore_put.sh \
63 test_namestore_put_stdin.sh \
64 test_namestore_lookup.sh \
65 test_namestore_delete.sh \
66 test_namestore_zonefile_import.sh
67
68EXTRA_DIST = \
69 example_zonefile \
70 $(check_SCRIPTS)
diff --git a/src/cli/namestore/example_zonefile b/src/cli/namestore/example_zonefile
new file mode 100644
index 000000000..5e380ff90
--- /dev/null
+++ b/src/cli/namestore/example_zonefile
@@ -0,0 +1,27 @@
1$ORIGIN example.com. ; designates the start of this zone file in the namespace
2$TTL 3600 ; default expiration time (in seconds) of all RRs without their own TTL value
3example.com. IN SOA ns.example.com. username.example.com. ( 2020091025 ; A comment
4 7200 ; Comment
5 ; empty line on purpose
6 3600
7 1209600
8 3600 )
9example.com. IN NS ns ; ns.example.com is a nameserver for example.com
10example.com. IN NS ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com
11example.com. IN MX 10 mail.example.com. ; mail.example.com is the mailserver for example.com
12@ IN MX 20 mail2.example.com. ; equivalent to above line, "@" represents zone origin
13@ IN MX 50 mail3 ; equivalent to above line, but using a relative host name
14b.example.com. IN A 192.0.2.1 ; IPv4 address for example.com
15 IN AAAA 2001:db8:10::1 ; IPv6 address for example.com
16ns IN A 192.0.2.2 ; IPv4 address for ns.example.com
17 IN AAAA 2001:db8:10::2 ; IPv6 address for ns.example.com
18www IN CNAME example.com. ; www.example.com is an alias for example.com
19wwwtest IN CNAME www ; wwwtest.example.com is another alias for www.example.com
20mail IN A 192.0.2.3 ; IPv4 address for mail.example.com
21mail2 IN A 192.0.2.4 ; IPv4 address for mail2.example.com
22mail3 IN A 192.0.2.5 ; IPv4 address for mail3.example.com
23
24mail3 IN TXT "This is ; quoted" ; A quoted comment separator
25$ORIGIN example.de.
26www2 IN A 192.0.2.7 ; IPv4 address for www2.example.de
27
diff --git a/src/cli/namestore/gnunet-namestore-dbtool.c b/src/cli/namestore/gnunet-namestore-dbtool.c
new file mode 100644
index 000000000..835d7a228
--- /dev/null
+++ b/src/cli/namestore/gnunet-namestore-dbtool.c
@@ -0,0 +1,199 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file gnunet-namestore-dbtool.c
22 * @brief command line tool to manipulate the database backends for the namestore
23 * @author Martin Schanzenbach
24 *
25 */
26#include "platform.h"
27#include <gnunet_util_lib.h>
28#include <gnunet_namestore_plugin.h>
29
30/**
31 * Name of the plugin argument
32 */
33static char *pluginname;
34
35/**
36 * Reset argument
37 */
38static int reset;
39
40/**
41 * Initialize argument
42 */
43static int init;
44
45/**
46 * Return code
47 */
48static int ret = 0;
49
50/**
51 * Task run on shutdown. Cleans up everything.
52 *
53 * @param cls unused
54 */
55static void
56do_shutdown (void *cls)
57{
58 (void) cls;
59 if (NULL != pluginname)
60 GNUNET_free (pluginname);
61}
62
63
64/**
65 * Main function that will be run.
66 *
67 * @param cls closure
68 * @param args remaining command-line arguments
69 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
70 * @param cfg configuration
71 */
72static void
73run (void *cls,
74 char *const *args,
75 const char *cfgfile,
76 const struct GNUNET_CONFIGURATION_Handle *cfg)
77{
78 char *db_lib_name;
79 struct GNUNET_NAMESTORE_PluginFunctions *plugin;
80
81 (void) cls;
82 (void) args;
83 (void) cfgfile;
84 if (NULL != args[0])
85 GNUNET_log (
86 GNUNET_ERROR_TYPE_WARNING,
87 _ ("Superfluous command line arguments (starting with `%s') ignored\n"),
88 args[0]);
89
90 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
91 (void *) cfg);
92 if (NULL == pluginname)
93 {
94 fprintf (stderr, "No plugin given!\n");
95 ret = 1;
96 GNUNET_SCHEDULER_shutdown ();
97 return;
98 }
99 GNUNET_asprintf (&db_lib_name,
100 "libgnunet_plugin_namestore_%s",
101 pluginname);
102 plugin = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
103 if (NULL == plugin)
104 {
105 fprintf (stderr,
106 "Failed to load %s!\n",
107 db_lib_name);
108 ret = 1;
109 GNUNET_SCHEDULER_shutdown ();
110 GNUNET_free (db_lib_name);
111 return;
112 }
113 if (reset)
114 {
115 if (GNUNET_OK !=
116 plugin->drop_tables (plugin->cls))
117 {
118 fprintf (stderr,
119 "Failed to reset database\n");
120 ret = 1;
121 GNUNET_free (db_lib_name);
122 GNUNET_SCHEDULER_shutdown ();
123 return;
124 }
125 }
126 if (init || reset)
127 {
128 if (GNUNET_OK !=
129 plugin->create_tables (plugin->cls))
130 {
131 fprintf (stderr,
132 "Failed to initialize database\n");
133 ret = 1;
134 GNUNET_free (db_lib_name);
135 GNUNET_SCHEDULER_shutdown ();
136 return;
137 }
138 }
139 GNUNET_SCHEDULER_shutdown ();
140 GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name,
141 plugin));
142 GNUNET_free (db_lib_name);
143}
144
145
146/**
147 * The main function for gnunet-namestore-dbtool.
148 *
149 * @param argc number of arguments from the command line
150 * @param argv command line arguments
151 * @return 0 ok, 1 on error
152 */
153int
154main (int argc, char *const *argv)
155{
156 struct GNUNET_GETOPT_CommandLineOption options[] = {
157 GNUNET_GETOPT_option_flag ('i', "init",
158 gettext_noop ("initialize database"),
159 &init),
160 GNUNET_GETOPT_option_flag ('r',
161 "reset",
162 gettext_noop (
163 "reset database (DANGEROUS: All existing data is lost!"),
164 &reset),
165 GNUNET_GETOPT_option_string (
166 'p',
167 "plugin",
168 "PLUGIN",
169 gettext_noop (
170 "the namestore plugin to work with, e.g. 'sqlite'"),
171 &pluginname),
172 GNUNET_GETOPT_OPTION_END
173 };
174 int lret;
175
176 if (GNUNET_OK !=
177 GNUNET_STRINGS_get_utf8_args (argc, argv,
178 &argc, &argv))
179 return 2;
180
181 GNUNET_log_setup ("gnunet-namestore-dbtool",
182 "WARNING",
183 NULL);
184 if (GNUNET_OK !=
185 (lret = GNUNET_PROGRAM_run (argc,
186 argv,
187 "gnunet-namestore-dbtool",
188 _ (
189 "GNUnet namestore database manipulation tool"),
190 options,
191 &run,
192 NULL)))
193 {
194 GNUNET_free_nz ((void *) argv);
195 return lret;
196 }
197 GNUNET_free_nz ((void *) argv);
198 return ret;
199}
diff --git a/src/cli/namestore/gnunet-namestore-zonefile.c b/src/cli/namestore/gnunet-namestore-zonefile.c
new file mode 100644
index 000000000..d43e88006
--- /dev/null
+++ b/src/cli/namestore/gnunet-namestore-zonefile.c
@@ -0,0 +1,729 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file gnunet-namestore-dbtool.c
22 * @brief command line tool to manipulate the database backends for the namestore
23 * @author Martin Schanzenbach
24 *
25 */
26#include "platform.h"
27#include <gnunet_util_lib.h>
28#include <gnunet_namestore_plugin.h>
29
30#define MAX_RECORDS_PER_NAME 50
31
32/**
33 * Maximum length of a zonefile line
34 */
35#define MAX_ZONEFILE_LINE_LEN 4096
36
37/**
38 * FIXME: Soft limit this?
39 */
40#define MAX_ZONEFILE_RECORD_DATA_LEN 2048
41
42/**
43 * The record data under a single label. Reused.
44 * Hard limit.
45 */
46static struct GNUNET_GNSRECORD_Data rd[MAX_RECORDS_PER_NAME];
47
48/**
49 * Current record $TTL to use
50 */
51static struct GNUNET_TIME_Relative ttl;
52
53/**
54 * Current origin
55 */
56static char origin[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
57
58/**
59 * Number of records for currently parsed set
60 */
61static unsigned int rd_count = 0;
62
63/**
64 * Return code
65 */
66static int ret = 0;
67
68/**
69 * Name of the ego
70 */
71static char *ego_name = NULL;
72
73/**
74 * Currently read line or NULL on EOF
75 */
76static char *res;
77
78/**
79 * Statistics, how many published record sets
80 */
81static unsigned int published_sets = 0;
82
83/**
84 * Statistics, how many records published in aggregate
85 */
86static unsigned int published_records = 0;
87
88
89/**
90 * Handle to identity lookup.
91 */
92static struct GNUNET_IDENTITY_EgoLookup *el;
93
94/**
95 * Private key for the our zone.
96 */
97static struct GNUNET_CRYPTO_PrivateKey zone_pkey;
98
99/**
100 * Queue entry for the 'add' operation.
101 */
102static struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
103
104/**
105 * Handle to the namestore.
106 */
107static struct GNUNET_NAMESTORE_Handle *ns;
108
109/**
110 * Origin create operations
111 */
112static struct GNUNET_IDENTITY_Operation *id_op;
113
114/**
115 * Handle to IDENTITY
116 */
117static struct GNUNET_IDENTITY_Handle *id;
118
119/**
120 * Current configurataion
121 */
122static const struct GNUNET_CONFIGURATION_Handle *cfg;
123
124/**
125 * Scheduled parse task
126 */
127static struct GNUNET_SCHEDULER_Task *parse_task;
128
129/**
130 * The current state of the parser
131 */
132static int state;
133
134enum ZonefileImportState
135{
136
137 /* Uninitialized */
138 ZS_READY,
139
140 /* The initial state */
141 ZS_ORIGIN_SET,
142
143 /* The $ORIGIN has changed */
144 ZS_ORIGIN_CHANGED,
145
146 /* The record name/label has changed */
147 ZS_NAME_CHANGED
148
149};
150
151
152
153/**
154 * Task run on shutdown. Cleans up everything.
155 *
156 * @param cls unused
157 */
158static void
159do_shutdown (void *cls)
160{
161 (void) cls;
162 if (NULL != ego_name)
163 GNUNET_free (ego_name);
164 if (NULL != el)
165 {
166 GNUNET_IDENTITY_ego_lookup_cancel (el);
167 el = NULL;
168 }
169 if (NULL != ns_qe)
170 GNUNET_NAMESTORE_cancel (ns_qe);
171 if (NULL != id_op)
172 GNUNET_IDENTITY_cancel (id_op);
173 if (NULL != ns)
174 GNUNET_NAMESTORE_disconnect (ns);
175 if (NULL != id)
176 GNUNET_IDENTITY_disconnect (id);
177 for (int i = 0; i < rd_count; i++)
178 {
179 void *rd_ptr = (void*) rd[i].data;
180 GNUNET_free (rd_ptr);
181 }
182 if (NULL != parse_task)
183 GNUNET_SCHEDULER_cancel (parse_task);
184}
185
186static void
187parse (void *cls);
188
189static char*
190trim (char *line)
191{
192 char *ltrimmed = line;
193 int ltrimmed_len;
194 int quoted = 0;
195
196 // Trim all whitespace to the left
197 while (*ltrimmed == ' ')
198 ltrimmed++;
199 ltrimmed_len = strlen (ltrimmed);
200 // Find the first occurence of an unqoted ';', which is our comment
201 for (int i = 0; i < ltrimmed_len; i++)
202 {
203 if (ltrimmed[i] == '"')
204 quoted = ! quoted;
205 if ((ltrimmed[i] != ';') || quoted)
206 continue;
207 ltrimmed[i] = '\0';
208 }
209 ltrimmed_len = strlen (ltrimmed);
210 // Remove trailing whitespace
211 for (int i = ltrimmed_len; i > 0; i--)
212 {
213 if (ltrimmed[i - 1] != ' ')
214 break;
215 ltrimmed[i - 1] = '\0';
216 }
217 ltrimmed_len = strlen (ltrimmed);
218 if (ltrimmed[ltrimmed_len - 1] == '\n')
219 ltrimmed[ltrimmed_len - 1] = ' ';
220 return ltrimmed;
221}
222
223static char*
224next_token (char *token)
225{
226 char *next = token;
227 while (*next == ' ')
228 next++;
229 return next;
230}
231
232static int
233parse_ttl (char *token, struct GNUNET_TIME_Relative *ttl)
234{
235 char *next;
236 unsigned int ttl_tmp;
237
238 next = strchr (token, ';');
239 if (NULL != next)
240 next[0] = '\0';
241 next = strchr (token, ' ');
242 if (NULL != next)
243 next[0] = '\0';
244 if (1 != sscanf (token, "%u", &ttl_tmp))
245 {
246 fprintf (stderr, "Unable to parse TTL `%s'\n", token);
247 return GNUNET_SYSERR;
248 }
249 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TTL is: %u\n", ttl_tmp);
250 ttl->rel_value_us = ttl_tmp * 1000 * 1000;
251 return GNUNET_OK;
252}
253
254static int
255parse_origin (char *token, char *origin)
256{
257 char *next;
258 next = strchr (token, ';');
259 if (NULL != next)
260 next[0] = '\0';
261 next = strchr (token, ' ');
262 if (NULL != next)
263 next[0] = '\0';
264 strcpy (origin, token);
265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin is: %s\n", origin);
266 return GNUNET_OK;
267}
268
269static void
270origin_create_cb (void *cls, const struct GNUNET_CRYPTO_PrivateKey *pk,
271 enum GNUNET_ErrorCode ec)
272{
273 id_op = NULL;
274 if (GNUNET_EC_NONE != ec)
275 {
276 fprintf (stderr, "Error: %s\n", GNUNET_ErrorCode_get_hint (ec));
277 ret = 1;
278 GNUNET_SCHEDULER_shutdown ();
279 return;
280 }
281 state = ZS_ORIGIN_SET;
282 zone_pkey = *pk;
283 parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL);
284}
285
286static void
287origin_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
288{
289
290 el = NULL;
291
292 if (NULL == ego)
293 {
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "$ORIGIN %s does not exist, creating...\n", ego_name);
296 id_op = GNUNET_IDENTITY_create (id, ego_name, NULL,
297 GNUNET_PUBLIC_KEY_TYPE_ECDSA, // FIXME make configurable
298 origin_create_cb,
299 NULL);
300 return;
301 }
302 state = ZS_ORIGIN_SET;
303 zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
304 parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL);
305}
306
307static void
308add_continuation (void *cls, enum GNUNET_ErrorCode ec)
309{
310 ns_qe = NULL;
311 if (GNUNET_EC_NONE != ec)
312 {
313 fprintf (stderr,
314 _ ("Failed to store records...\n"));
315 GNUNET_SCHEDULER_shutdown ();
316 ret = -1;
317 }
318 if (ZS_ORIGIN_CHANGED == state)
319 {
320 if (NULL != ego_name)
321 GNUNET_free (ego_name);
322 ego_name = GNUNET_strdup (origin);
323 if (ego_name[strlen (ego_name) - 1] == '.')
324 ego_name[strlen (ego_name) - 1] = '\0';
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Changing origin to %s\n", ego_name);
327 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name,
328 &origin_lookup_cb, NULL);
329 return;
330 }
331 parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL);
332}
333
334
335
336/**
337 * Main function that will be run.
338 *
339 * TODO:
340 * - We must assume that names are not repeated later in the zonefile because
341 * our _store APIs are replacing. No sure if that is common in zonefiles.
342 * - We must only actually store a record set when the name to store changes or
343 * the end of the file is reached.
344 * that way we can group them and add (see above).
345 * - We need to hope our string formats are compatible, but seems ok.
346 *
347 * @param cls closure
348 * @param args remaining command-line arguments
349 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
350 * @param cfg configuration
351 */
352static void
353parse (void *cls)
354{
355 char buf[MAX_ZONEFILE_LINE_LEN];
356 char payload[MAX_ZONEFILE_RECORD_DATA_LEN];
357 char *next;
358 char *token;
359 char *payload_pos;
360 static char lastname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH];
361 char newname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH];
362 void *data;
363 size_t data_size;
364 int ttl_line = 0;
365 int type;
366 int bracket_unclosed = 0;
367 int quoted = 0;
368
369 parse_task = NULL;
370 /* use filename provided as 1st argument (stdin by default) */
371 int ln = 0;
372 while ((res = fgets (buf, sizeof(buf), stdin))) /* read each line of input */
373 {
374 ln++;
375 ttl_line = 0;
376 token = trim (buf);
377 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
378 "Trimmed line (bracket %s): `%s'\n",
379 (bracket_unclosed > 0) ? "unclosed" : "closed",
380 token);
381 if ((0 == strlen (token)) ||
382 ((1 == strlen (token)) && (' ' == *token)))
383 continue; // I guess we can safely ignore blank lines
384 if (bracket_unclosed == 0)
385 {
386 /* Payload is already parsed */
387 payload_pos = payload;
388 /* Find space */
389 next = strchr (token, ' ');
390 if (NULL == next)
391 {
392 fprintf (stderr, "Error at line %u: %s\n", ln, token);
393 ret = 1;
394 GNUNET_SCHEDULER_shutdown ();
395 return;
396 }
397 next[0] = '\0';
398 next++;
399 if (0 == (strcmp (token, "$ORIGIN")))
400 {
401 state = ZS_ORIGIN_CHANGED;
402 token = next_token (next);
403 }
404 else if (0 == (strcmp (token, "$TTL")))
405 {
406 ttl_line = 1;
407 token = next_token (next);
408 }
409 else
410 {
411 if (0 == strcmp (token, "IN")) // Inherit name from before
412 {
413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414 "Old name: %s\n", lastname);
415 strcpy (newname, lastname);
416 token[strlen (token)] = ' ';
417 }
418 else if (token[strlen (token) - 1] != '.') // no fqdn
419 {
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token);
421 if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token))
422 {
423 fprintf (stderr,
424 _ ("Name `%s' is too long\n"),
425 token);
426 ret = 1;
427 GNUNET_SCHEDULER_shutdown ();
428 return;
429 }
430 strcpy (newname, token);
431 token = next_token (next);
432 }
433 else if (0 == strcmp (token, origin))
434 {
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: @\n");
436 strcpy (newname, "@");
437 token = next_token (next);
438 }
439 else
440 {
441 if (strlen (token) < strlen (origin))
442 {
443 fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin);
444 break; // FIXME error?
445 }
446 if (0 != strcmp (token + (strlen (token) - strlen (origin)), origin))
447 {
448 fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin);
449 break;
450 }
451 token[strlen (token) - strlen (origin) - 1] = '\0';
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token);
453 if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token))
454 {
455 fprintf (stderr,
456 _ ("Name `%s' is too long\n"),
457 token);
458 ret = 1;
459 GNUNET_SCHEDULER_shutdown ();
460 return;
461 }
462 strcpy (newname, token);
463 token = next_token (next);
464 }
465 if (0 != strcmp (newname, lastname) &&
466 (0 < rd_count))
467 {
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Name changed %s->%s, storing record set of %u elements\n",
470 lastname, newname,
471 rd_count);
472 state = ZS_NAME_CHANGED;
473 }
474 else {
475 strcpy (lastname, newname);
476 }
477 }
478
479 if (ttl_line)
480 {
481 if (GNUNET_SYSERR == parse_ttl (token, &ttl))
482 {
483 fprintf (stderr, _ ("Failed to parse $TTL\n"));
484 ret = 1;
485 GNUNET_SCHEDULER_shutdown ();
486 return;
487 }
488 continue;
489 }
490 if (ZS_ORIGIN_CHANGED == state)
491 {
492 if (GNUNET_SYSERR == parse_origin (token, origin))
493 {
494 fprintf (stderr, _ ("Failed to parse $ORIGIN from %s\n"), token);
495 ret = 1;
496 GNUNET_SCHEDULER_shutdown ();
497 return;
498 }
499 break;
500 }
501 if (ZS_READY == state)
502 {
503 fprintf (stderr,
504 _ (
505 "You must provide $ORIGIN in your zonefile or via arguments (--zone)!\n"));
506 ret = 1;
507 GNUNET_SCHEDULER_shutdown ();
508 return;
509 }
510 // This is a record, let's go
511 if (MAX_RECORDS_PER_NAME == rd_count)
512 {
513 fprintf (stderr,
514 _ ("Only %u records per unique name supported.\n"),
515 MAX_RECORDS_PER_NAME);
516 ret = 1;
517 GNUNET_SCHEDULER_shutdown ();
518 return;
519 }
520 rd[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
521 rd[rd_count].expiration_time = ttl.rel_value_us;
522 next = strchr (token, ' ');
523 if (NULL == next)
524 {
525 fprintf (stderr, "Error, last token: %s\n", token);
526 ret = 1;
527 GNUNET_SCHEDULER_shutdown ();
528 break;
529 }
530 next[0] = '\0';
531 next++;
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "class is: %s\n", token);
533 while (*next == ' ')
534 next++;
535 token = next;
536 next = strchr (token, ' ');
537 if (NULL == next)
538 {
539 fprintf (stderr, "Error\n");
540 break;
541 }
542 next[0] = '\0';
543 next++;
544 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "type is: %s\n", token);
545 type = GNUNET_GNSRECORD_typename_to_number (token);
546 rd[rd_count].record_type = type;
547 while (*next == ' ')
548 next++;
549 token = next;
550 }
551 for (int i = 0; i < strlen (token); i++)
552 {
553 if (token[i] == '"')
554 quoted = ! quoted;
555 if ((token[i] == '(') && ! quoted)
556 bracket_unclosed++;
557 if ((token[i] == ')') && ! quoted)
558 bracket_unclosed--;
559 }
560 memcpy (payload_pos, token, strlen (token));
561 payload_pos += strlen (token);
562 if (bracket_unclosed > 0)
563 {
564 *payload_pos = ' ';
565 payload_pos++;
566 continue;
567 }
568 *payload_pos = '\0';
569 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "data is: %s\n\n", payload);
570 if (GNUNET_OK !=
571 GNUNET_GNSRECORD_string_to_value (type, payload,
572 &data,
573 &data_size))
574 {
575 fprintf (stderr,
576 _ ("Data `%s' invalid\n"),
577 payload);
578 ret = 1;
579 GNUNET_SCHEDULER_shutdown ();
580 return;
581 }
582 rd[rd_count].data = data;
583 rd[rd_count].data_size = data_size;
584 if (ZS_NAME_CHANGED == state)
585 break;
586 rd_count++;
587 }
588 if (rd_count > 0)
589 {
590 ns_qe = GNUNET_NAMESTORE_record_set_store (ns,
591 &zone_pkey,
592 lastname,
593 rd_count,
594 rd,
595 &add_continuation,
596 NULL);
597 published_sets++;
598 published_records += rd_count;
599 for (int i = 0; i < rd_count; i++)
600 {
601 data = (void*) rd[i].data;
602 GNUNET_free (data);
603 }
604 if (ZS_NAME_CHANGED == state)
605 {
606 rd[0] = rd[rd_count]; // recover last rd parsed.
607 rd_count = 1;
608 strcpy (lastname, newname);
609 state = ZS_ORIGIN_SET;
610 }
611 else
612 rd_count = 0;
613 return;
614 }
615 if (ZS_ORIGIN_CHANGED == state)
616 {
617 if (NULL != ego_name)
618 GNUNET_free (ego_name);
619 ego_name = GNUNET_strdup (origin);
620 if (ego_name[strlen (ego_name) - 1] == '.')
621 ego_name[strlen (ego_name) - 1] = '\0';
622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623 "Changing origin to %s\n", ego_name);
624 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name,
625 &origin_lookup_cb, NULL);
626 return;
627 }
628 printf ("Published %u records sets with total %u records\n",
629 published_sets, published_records);
630 GNUNET_SCHEDULER_shutdown ();
631}
632
633
634static void
635identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
636{
637
638 el = NULL;
639 if (NULL == ego)
640 {
641 if (NULL != ego_name)
642 {
643 fprintf (stderr,
644 _ ("Ego `%s' not known to identity service\n"),
645 ego_name);
646
647 }
648 GNUNET_SCHEDULER_shutdown ();
649 ret = -1;
650 return;
651 }
652 zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
653 sprintf (origin, "%s.", ego_name);
654 state = ZS_ORIGIN_SET;
655 parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL);
656}
657
658
659static void
660run (void *cls,
661 char *const *args,
662 const char *cfgfile,
663 const struct GNUNET_CONFIGURATION_Handle *_cfg)
664{
665 cfg = _cfg;
666 ns = GNUNET_NAMESTORE_connect (cfg);
667 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg);
668 if (NULL == ns)
669 {
670 fprintf (stderr,
671 _ ("Failed to connect to NAMESTORE\n"));
672 return;
673 }
674 id = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
675 if (NULL == id)
676 {
677 fprintf (stderr,
678 _ ("Failed to connect to IDENTITY\n"));
679 return;
680 }
681 if (NULL != ego_name)
682 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg);
683 else
684 parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL);
685 state = ZS_READY;
686}
687
688
689/**
690 * The main function for gnunet-namestore-dbtool.
691 *
692 * @param argc number of arguments from the command line
693 * @param argv command line arguments
694 * @return 0 ok, 1 on error
695 */
696int
697main (int argc, char *const *argv)
698{
699 struct GNUNET_GETOPT_CommandLineOption options[] = {
700 GNUNET_GETOPT_option_string ('z',
701 "zone",
702 "EGO",
703 gettext_noop (
704 "name of the ego controlling the zone"),
705 &ego_name),
706 GNUNET_GETOPT_OPTION_END
707 };
708 int lret;
709
710 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
711 return 2;
712
713 GNUNET_log_setup ("gnunet-namestore-dbtool", "WARNING", NULL);
714 if (GNUNET_OK !=
715 (lret = GNUNET_PROGRAM_run (argc,
716 argv,
717 "gnunet-namestore-zonefile",
718 _ (
719 "GNUnet namestore database manipulation tool"),
720 options,
721 &run,
722 NULL)))
723 {
724 GNUNET_free_nz ((void *) argv);
725 return lret;
726 }
727 GNUNET_free_nz ((void *) argv);
728 return ret;
729}
diff --git a/src/cli/namestore/gnunet-namestore.c b/src/cli/namestore/gnunet-namestore.c
new file mode 100644
index 000000000..6c0890a43
--- /dev/null
+++ b/src/cli/namestore/gnunet-namestore.c
@@ -0,0 +1,2118 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2014, 2019 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file gnunet-namestore.c
22 * @brief command line tool to manipulate the local zone
23 * @author Christian Grothoff
24 *
25 * TODO:
26 * - test
27 */
28#include "platform.h"
29#include <gnunet_util_lib.h>
30#include <gnunet_identity_service.h>
31#include <gnunet_gnsrecord_lib.h>
32#include <gnunet_gns_service.h>
33#include <gnunet_namestore_service.h>
34#include <inttypes.h>
35
36/**
37 * The upper bound for the zone iteration interval
38 * (per record).
39 */
40#define WARN_RELATIVE_EXPIRATION_LIMIT GNUNET_TIME_relative_multiply ( \
41 GNUNET_TIME_UNIT_MINUTES, 15)
42
43/**
44 * Entry in record set for bulk processing.
45 */
46struct RecordSetEntry
47{
48 /**
49 * Kept in a linked list.
50 */
51 struct RecordSetEntry *next;
52
53 /**
54 * The record to add/remove.
55 */
56 struct GNUNET_GNSRECORD_Data record;
57};
58
59/**
60 * The record marked for deletion
61 */
62struct MarkedRecord
63{
64 /**
65 * DLL
66 */
67 struct MarkedRecord *next;
68
69 /**
70 * DLL
71 */
72 struct MarkedRecord *prev;
73
74 /**
75 * Ego Identifier
76 */
77 char *name;
78
79 /**
80 * The zone key
81 */
82 struct GNUNET_CRYPTO_PrivateKey key;
83};
84
85/**
86 * The default namestore ego
87 */
88struct EgoEntry
89{
90 /**
91 * DLL
92 */
93 struct EgoEntry *next;
94
95 /**
96 * DLL
97 */
98 struct EgoEntry *prev;
99
100 /**
101 * Ego Identifier
102 */
103 char *identifier;
104
105 /**
106 * The Ego
107 */
108 struct GNUNET_IDENTITY_Ego *ego;
109};
110
111/**
112 * Handle to the namestore.
113 */
114static struct GNUNET_NAMESTORE_Handle *ns;
115
116/**
117 * Private key for the our zone.
118 */
119static struct GNUNET_CRYPTO_PrivateKey zone_pkey;
120
121/**
122 * Identity service handle
123 */
124static struct GNUNET_IDENTITY_Handle *idh;
125
126/**
127 * Name of the ego controlling the zone.
128 */
129static char *ego_name;
130
131/**
132 * Queue entry for the 'add-uri' operation.
133 */
134static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
135
136/**
137 * Queue entry for the 'add' operation.
138 */
139static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
140
141/**
142 * Queue entry for the 'lookup' operation.
143 */
144static struct GNUNET_NAMESTORE_QueueEntry *get_qe;
145
146/**
147 * Queue entry for the 'reverse lookup' operation (in combination with a name).
148 */
149static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
150
151/**
152 * Marked record list
153 */
154static struct MarkedRecord *marked_head;
155
156/**
157 * Marked record list
158 */
159static struct MarkedRecord *marked_tail;
160
161/**
162 * Configuration handle
163 */
164const struct GNUNET_CONFIGURATION_Handle *cfg;
165
166/**
167 * Ego list
168 */
169static struct EgoEntry *ego_head;
170
171/**
172 * Ego list
173 */
174static struct EgoEntry *ego_tail;
175
176/**
177 * List iterator for the 'list' operation.
178 */
179static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
180
181/**
182 * Run in read from stdin mode.
183 */
184static int read_from_stdin;
185
186/**
187 * Desired action is to list records.
188 */
189static int list;
190
191/**
192 * Desired action is to add a record.
193 */
194static int add;
195
196/**
197 * Desired action is to remove a record.
198 */
199static int del;
200
201/**
202 * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
203 */
204static int is_public;
205
206/**
207 * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW)
208 */
209static int is_shadow;
210
211/**
212 * Is record a maintenance record (#GNUNET_GNSRECORD_RF_MAINTENANCE)
213 */
214static int is_maintenance;
215
216/**
217 * Filter private records
218 */
219static int omit_private;
220
221/**
222 * Output in recordline format
223 */
224static int output_recordline;
225
226
227/**
228 * Purge zone contents
229 */
230static int purge_zone;
231
232/**
233 * Do not filter maintenance records
234 */
235static int include_maintenance;
236
237/**
238 * Purge orphaned records
239 */
240static int purge_orphaned;
241
242/**
243 * List records and zone keys of orphaned records
244 */
245static int list_orphaned;
246
247/**
248 * Queue entry for the 'del' operation.
249 */
250static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
251
252/**
253 * Queue entry for the 'set/replace' operation.
254 */
255static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
256
257/**
258 * Queue entry for begin/commit
259 */
260static struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
261
262/**
263 * Name of the records to add/list/remove.
264 */
265static char *name;
266
267/**
268 * Value of the record to add/remove.
269 */
270static char *value;
271
272/**
273 * URI to import.
274 */
275static char *uri;
276
277/**
278 * Reverse lookup to perform.
279 */
280static char *reverse_pkey;
281
282/**
283 * Type of the record to add/remove, NULL to remove all.
284 */
285static char *typestring;
286
287/**
288 * Desired expiration time.
289 */
290static char *expirationstring;
291
292/**
293 * Desired nick name.
294 */
295static char *nickstring;
296
297/**
298 * Global return value
299 */
300static int ret;
301
302/**
303 * Type string converted to DNS type value.
304 */
305static uint32_t type;
306
307/**
308 * Value in binary format.
309 */
310static void *data;
311
312/**
313 * Number of bytes in #data.
314 */
315static size_t data_size;
316
317/**
318 * Expiration string converted to numeric value.
319 */
320static uint64_t etime;
321
322/**
323 * Is expiration time relative or absolute time?
324 */
325static int etime_is_rel = GNUNET_SYSERR;
326
327/**
328 * Monitor handle.
329 */
330static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
331
332/**
333 * Enables monitor mode.
334 */
335static int monitor;
336
337/**
338 * Entry in record set for processing records in bulk.
339 */
340static struct RecordSetEntry *recordset;
341
342/**
343 * Purge task
344 */
345static struct GNUNET_SCHEDULER_Task *purge_task;
346
347/**
348 * Parse expiration time.
349 *
350 * @param expirationstring text to parse
351 * @param[out] etime_is_rel set to #GNUNET_YES if time is relative
352 * @param[out] etime set to expiration time (abs or rel)
353 * @return #GNUNET_OK on success
354 */
355static int
356parse_expiration (const char *expirationstring,
357 int *etime_is_rel,
358 uint64_t *etime)
359{
360 struct GNUNET_TIME_Relative etime_rel;
361 struct GNUNET_TIME_Absolute etime_abs;
362
363 if (0 == strcmp (expirationstring, "never"))
364 {
365 *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
366 *etime_is_rel = GNUNET_NO;
367 return GNUNET_OK;
368 }
369 if (GNUNET_OK ==
370 GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel))
371 {
372 *etime_is_rel = GNUNET_YES;
373 *etime = etime_rel.rel_value_us;
374 if (GNUNET_TIME_relative_cmp (etime_rel, <, WARN_RELATIVE_EXPIRATION_LIMIT))
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
377 "Relative expiration times of less than %s are not recommended. To improve availability, consider increasing this value.\n",
378 GNUNET_STRINGS_relative_time_to_string (
379 WARN_RELATIVE_EXPIRATION_LIMIT, GNUNET_NO));
380 }
381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382 "Storing record with relative expiration time of %s\n",
383 GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO));
384 return GNUNET_OK;
385 }
386 if (GNUNET_OK ==
387 GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs))
388 {
389 *etime_is_rel = GNUNET_NO;
390 *etime = etime_abs.abs_value_us;
391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392 "Storing record with absolute expiration time of %s\n",
393 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
394 return GNUNET_OK;
395 }
396 return GNUNET_SYSERR;
397}
398
399
400static int
401parse_recordline (const char *line)
402{
403 struct RecordSetEntry **head = &recordset;
404 struct RecordSetEntry *r;
405 struct GNUNET_GNSRECORD_Data record;
406 char *cp;
407 char *tok;
408 char *saveptr;
409 void *raw_data;
410
411 cp = GNUNET_strdup (line);
412 tok = strtok_r (cp, " ", &saveptr);
413 if (NULL == tok)
414 {
415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
416 _ ("Missing entries in record line `%s'.\n"),
417 line);
418 GNUNET_free (cp);
419 return GNUNET_SYSERR;
420 }
421 record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
422 if (UINT32_MAX == record.record_type)
423 {
424 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Unknown record type `%s'\n"), tok);
425 GNUNET_free (cp);
426 return GNUNET_SYSERR;
427 }
428 tok = strtok_r (NULL, " ", &saveptr);
429 if (NULL == tok)
430 {
431 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
432 _ ("Empty record line argument is not allowed.\n"));
433 GNUNET_free (cp);
434 return GNUNET_SYSERR;
435 }
436 if (1 != sscanf (tok, "%" SCNu64, &record.expiration_time))
437 {
438 fprintf (stderr,
439 _ ("Error parsing expiration time %s.\n"), tok);
440 GNUNET_free (cp);
441 return GNUNET_SYSERR;
442 }
443 tok = strtok_r (NULL, " ", &saveptr);
444 if (NULL == tok)
445 {
446 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
447 _ ("Empty record line argument is not allowed.\n"));
448 GNUNET_free (cp);
449 return GNUNET_SYSERR;
450 }
451 record.flags = GNUNET_GNSRECORD_RF_NONE;
452 if (NULL != strchr (tok, (unsigned char) 'r'))
453 record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
454 if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */
455 record.flags |= GNUNET_GNSRECORD_RF_PRIVATE;
456 if (NULL != strchr (tok, (unsigned char) 'S'))
457 record.flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL;
458 if (NULL != strchr (tok, (unsigned char) 's'))
459 record.flags |= GNUNET_GNSRECORD_RF_SHADOW;
460 if (NULL != strchr (tok, (unsigned char) 'C'))
461 record.flags |= GNUNET_GNSRECORD_RF_CRITICAL;
462 tok += strlen (tok) + 1;
463 if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (record.record_type,
464 tok,
465 &raw_data,
466 &record.data_size))
467 {
468 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469 _ ("Invalid record data for type %s: `%s'.\n"),
470 GNUNET_GNSRECORD_number_to_typename (record.record_type),
471 tok);
472 GNUNET_free (cp);
473 return GNUNET_SYSERR;
474 }
475 GNUNET_free (cp);
476
477 r = GNUNET_malloc (sizeof(struct RecordSetEntry) + record.data_size);
478 r->next = *head;
479 record.data = &r[1];
480 memcpy (&r[1], raw_data, record.data_size);
481 GNUNET_free (raw_data);
482 r->record = record;
483 *head = r;
484 return GNUNET_OK;
485}
486
487
488static void
489reset_handles (void)
490{
491 struct MarkedRecord *mrec;
492 struct MarkedRecord *mrec_tmp;
493 struct RecordSetEntry *rs_entry;
494
495 rs_entry = recordset;
496 while (NULL != (rs_entry = recordset))
497 {
498 recordset = recordset->next;
499 GNUNET_free (rs_entry);
500 }
501 recordset = NULL;
502 if (NULL != ego_name)
503 {
504 GNUNET_free (ego_name);
505 ego_name = NULL;
506 }
507 if (NULL != name)
508 {
509 GNUNET_free (name);
510 name = NULL;
511 }
512 if (NULL != value)
513 {
514 GNUNET_free (value);
515 value = NULL;
516 }
517 if (NULL != uri)
518 {
519 GNUNET_free (uri);
520 uri = NULL;
521 }
522 if (NULL != expirationstring)
523 {
524 GNUNET_free (expirationstring);
525 expirationstring = NULL;
526 }
527 if (NULL != purge_task)
528 {
529 GNUNET_SCHEDULER_cancel (purge_task);
530 purge_task = NULL;
531 }
532 for (mrec = marked_head; NULL != mrec;)
533 {
534 mrec_tmp = mrec;
535 mrec = mrec->next;
536 GNUNET_free (mrec_tmp->name);
537 GNUNET_free (mrec_tmp);
538 }
539 if (NULL != list_it)
540 {
541 GNUNET_NAMESTORE_zone_iteration_stop (list_it);
542 list_it = NULL;
543 }
544 if (NULL != add_qe)
545 {
546 GNUNET_NAMESTORE_cancel (add_qe);
547 add_qe = NULL;
548 }
549 if (NULL != set_qe)
550 {
551 GNUNET_NAMESTORE_cancel (set_qe);
552 set_qe = NULL;
553 }
554 if (NULL != add_qe_uri)
555 {
556 GNUNET_NAMESTORE_cancel (add_qe_uri);
557 add_qe_uri = NULL;
558 }
559 if (NULL != get_qe)
560 {
561 GNUNET_NAMESTORE_cancel (get_qe);
562 get_qe = NULL;
563 }
564 if (NULL != del_qe)
565 {
566 GNUNET_NAMESTORE_cancel (del_qe);
567 del_qe = NULL;
568 }
569 if (NULL != reverse_qe)
570 {
571 GNUNET_NAMESTORE_cancel (reverse_qe);
572 reverse_qe = NULL;
573 }
574 memset (&zone_pkey, 0, sizeof(zone_pkey));
575 if (NULL != zm)
576 {
577 GNUNET_NAMESTORE_zone_monitor_stop (zm);
578 zm = NULL;
579 }
580 if (NULL != data)
581 {
582 GNUNET_free (data);
583 data = NULL;
584 }
585 if (NULL != typestring)
586 {
587 GNUNET_free (typestring);
588 typestring = NULL;
589 }
590 list = 0;
591 is_public = 0;
592 is_shadow = 0;
593 is_maintenance = 0;
594 purge_zone = 0;
595}
596
597
598/**
599 * Task run on shutdown. Cleans up everything.
600 *
601 * @param cls unused
602 */
603static void
604do_shutdown (void *cls)
605{
606 struct EgoEntry *ego_entry;
607 struct EgoEntry *ego_tmp;
608 (void) cls;
609
610 reset_handles ();
611 if (NULL != ns_qe)
612 {
613 GNUNET_NAMESTORE_cancel (ns_qe);
614 ns_qe = NULL;
615 }
616 if (NULL != ns)
617 {
618 GNUNET_NAMESTORE_disconnect (ns);
619 ns = NULL;
620 }
621 if (NULL != idh)
622 {
623 GNUNET_IDENTITY_disconnect (idh);
624 idh = NULL;
625 }
626 for (ego_entry = ego_head; NULL != ego_entry;)
627 {
628 ego_tmp = ego_entry;
629 ego_entry = ego_entry->next;
630 GNUNET_free (ego_tmp->identifier);
631 GNUNET_free (ego_tmp);
632 }
633}
634
635
636static void
637process_command_stdin ();
638
639
640static void
641finish_command (void)
642{
643 reset_handles ();
644 if (read_from_stdin)
645 {
646 process_command_stdin ();
647 return;
648 }
649 GNUNET_SCHEDULER_shutdown ();
650}
651
652
653static void
654add_continuation (void *cls, enum GNUNET_ErrorCode ec)
655{
656 struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
657
658 *qe = NULL;
659 if (GNUNET_EC_NONE != ec)
660 {
661 fprintf (stderr,
662 _ ("Adding record failed: %s\n"),
663 GNUNET_ErrorCode_get_hint (ec));
664 if (GNUNET_EC_NAMESTORE_RECORD_EXISTS != ec)
665 ret = 1;
666 }
667 ret = 0;
668 finish_command ();
669}
670
671
672static void
673del_continuation (void *cls, enum GNUNET_ErrorCode ec)
674{
675 (void) cls;
676 del_qe = NULL;
677 if (GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND == ec)
678 {
679 fprintf (stderr,
680 _ ("Deleting record failed: %s\n"), GNUNET_ErrorCode_get_hint (
681 ec));
682 }
683 finish_command ();
684}
685
686
687static void
688purge_next_record (void *cls);
689
690static void
691marked_deleted (void *cls, enum GNUNET_ErrorCode ec)
692{
693 del_qe = NULL;
694 if (GNUNET_EC_NONE != ec)
695 {
696 fprintf (stderr,
697 _ ("Deleting record failed: %s\n"),
698 GNUNET_ErrorCode_get_hint (ec));
699 }
700 purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL);
701}
702
703
704static void
705purge_next_record (void *cls)
706{
707 struct MarkedRecord *mrec;
708 purge_task = NULL;
709
710 if (NULL == marked_head)
711 {
712 ret = 0;
713 finish_command ();
714 return;
715 }
716 mrec = marked_head;
717 GNUNET_CONTAINER_DLL_remove (marked_head,
718 marked_tail,
719 mrec);
720 del_qe = GNUNET_NAMESTORE_record_set_store (ns,
721 &mrec->key,
722 mrec->name,
723 0, NULL,
724 &marked_deleted,
725 NULL);
726 GNUNET_free (mrec->name);
727 GNUNET_free (mrec);
728}
729
730
731/**
732 * Function called when we are done with a zone iteration.
733 */
734static void
735zone_iteration_finished (void *cls)
736{
737 (void) cls;
738 list_it = NULL;
739 if (purge_orphaned || purge_zone)
740 {
741 purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL);
742 return;
743 }
744 ret = 0;
745 finish_command ();
746}
747
748
749/**
750 * Function called when we encountered an error in a zone iteration.
751 */
752static void
753zone_iteration_error_cb (void *cls)
754{
755 (void) cls;
756 list_it = NULL;
757 fprintf (stderr, "Error iterating over zone\n");
758 ret = 1;
759 finish_command ();
760}
761
762
763static void
764collect_zone_records_to_purge (const struct
765 GNUNET_CRYPTO_PrivateKey *zone_key,
766 const char *rname,
767 unsigned int rd_len,
768 const struct GNUNET_GNSRECORD_Data *rd)
769{
770 struct MarkedRecord *mrec;
771
772 mrec = GNUNET_new (struct MarkedRecord);
773 mrec->key = *zone_key;
774 mrec->name = GNUNET_strdup (rname);
775 GNUNET_CONTAINER_DLL_insert (marked_head,
776 marked_tail,
777 mrec);
778}
779
780
781static void
782collect_orphans (const struct GNUNET_CRYPTO_PrivateKey *zone_key,
783 const char *rname,
784 unsigned int rd_len,
785 const struct GNUNET_GNSRECORD_Data *rd)
786{
787 struct EgoEntry *ego;
788 struct MarkedRecord *orphan;
789 int is_orphaned = 1;
790
791 for (ego = ego_head; NULL != ego; ego = ego->next)
792 {
793 if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego),
794 zone_key,
795 sizeof (*zone_key)))
796 {
797 is_orphaned = 0;
798 break;
799 }
800 }
801 if (is_orphaned)
802 {
803 orphan = GNUNET_new (struct MarkedRecord);
804 orphan->key = *zone_key;
805 orphan->name = GNUNET_strdup (rname);
806 GNUNET_CONTAINER_DLL_insert (marked_head,
807 marked_tail,
808 orphan);
809 }
810}
811
812
813/**
814 * Process a record that was stored in the namestore.
815 *
816 * @param rname name that is being mapped (at most 255 characters long)
817 * @param rd_len number of entries in @a rd array
818 * @param rd array of records with data to store
819 */
820static void
821display_record (const struct GNUNET_CRYPTO_PrivateKey *zone_key,
822 const char *rname,
823 unsigned int rd_len,
824 const struct GNUNET_GNSRECORD_Data *rd)
825{
826 const char *typestr;
827 char *s;
828 const char *ets;
829 struct GNUNET_TIME_Absolute at;
830 struct GNUNET_TIME_Relative rt;
831 struct EgoEntry *ego;
832 int have_record;
833 int is_orphaned = 1;
834 char *orphaned_str;
835
836 if ((NULL != name) && (0 != strcmp (name, rname)))
837 return;
838 have_record = GNUNET_NO;
839 for (unsigned int i = 0; i < rd_len; i++)
840 {
841 if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
842 (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)))
843 continue;
844 if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type))
845 continue;
846 have_record = GNUNET_YES;
847 break;
848 }
849 if (GNUNET_NO == have_record)
850 return;
851 for (ego = ego_head; NULL != ego; ego = ego->next)
852 {
853 if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego),
854 zone_key,
855 sizeof (*zone_key)))
856 {
857 is_orphaned = 0;
858 break;
859 }
860 }
861 if (list_orphaned && ! is_orphaned)
862 return;
863 if (! list_orphaned && is_orphaned)
864 return;
865 orphaned_str = GNUNET_CRYPTO_private_key_to_string (zone_key);
866 fprintf (stdout, "%s.%s:\n", rname, is_orphaned ? orphaned_str :
867 ego->identifier);
868 GNUNET_free (orphaned_str);
869 if (NULL != typestring)
870 type = GNUNET_GNSRECORD_typename_to_number (typestring);
871 else
872 type = GNUNET_GNSRECORD_TYPE_ANY;
873 for (unsigned int i = 0; i < rd_len; i++)
874 {
875 if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
876 (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)))
877 continue;
878 if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type))
879 continue;
880 typestr = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
881 s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
882 rd[i].data,
883 rd[i].data_size);
884 if (NULL == s)
885 {
886 fprintf (stdout,
887 _ ("\tCorrupt or unsupported record of type %u\n"),
888 (unsigned int) rd[i].record_type);
889 continue;
890 }
891 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
892 {
893 rt.rel_value_us = rd[i].expiration_time;
894 ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES);
895 }
896 else
897 {
898 at.abs_value_us = rd[i].expiration_time;
899 ets = GNUNET_STRINGS_absolute_time_to_string (at);
900 }
901 char flgstr[16];
902 sprintf (flgstr, "[%s%s%s%s%s]",
903 (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE) ? "" : "p",
904 (rd[i].flags & GNUNET_GNSRECORD_RF_SUPPLEMENTAL) ? "S" : "",
905 (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION) ? "r" : "",
906 (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW) ? "S" : "",
907 (rd[i].flags & GNUNET_GNSRECORD_RF_CRITICAL) ? "C" : "");
908 if (output_recordline)
909 fprintf (stdout,
910 " %s %" PRIu64 " %s %s\n",
911 typestr,
912 rd[i].expiration_time,
913 flgstr,
914 s);
915 else
916 fprintf (stdout,
917 "\t%s: %s (%s)\t%s\t%s\t%s\n",
918 typestr,
919 s,
920 ets,
921 (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)) ? "PRIVATE"
922 : "PUBLIC",
923 (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW)) ? "SHADOW"
924 : "",
925 (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_MAINTENANCE)) ?
926 "MAINTENANCE"
927 : "");
928 GNUNET_free (s);
929 }
930 // fprintf (stdout, "%s", "\n");
931}
932
933
934static void
935purge_zone_iterator (void *cls,
936 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
937 const char *rname,
938 unsigned int rd_len,
939 const struct GNUNET_GNSRECORD_Data *rd,
940 struct GNUNET_TIME_Absolute expiry)
941{
942 (void) cls;
943 (void) zone_key;
944 (void) expiry;
945 collect_zone_records_to_purge (zone_key, rname, rd_len, rd);
946 GNUNET_NAMESTORE_zone_iterator_next (list_it, 1);
947}
948
949
950static void
951purge_orphans_iterator (void *cls,
952 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
953 const char *rname,
954 unsigned int rd_len,
955 const struct GNUNET_GNSRECORD_Data *rd,
956 struct GNUNET_TIME_Absolute expiry)
957{
958 (void) cls;
959 (void) zone_key;
960 (void) expiry;
961 collect_orphans (zone_key, rname, rd_len, rd);
962 GNUNET_NAMESTORE_zone_iterator_next (list_it, 1);
963}
964
965
966/**
967 * Process a record that was stored in the namestore.
968 *
969 * @param cls closure
970 * @param zone_key private key of the zone
971 * @param rname name that is being mapped (at most 255 characters long)
972 * @param rd_len number of entries in @a rd array
973 * @param rd array of records with data to store
974 */
975static void
976display_record_iterator (void *cls,
977 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
978 const char *rname,
979 unsigned int rd_len,
980 const struct GNUNET_GNSRECORD_Data *rd,
981 struct GNUNET_TIME_Absolute expiry)
982{
983 (void) cls;
984 (void) zone_key;
985 (void) expiry;
986 display_record (zone_key, rname, rd_len, rd);
987 GNUNET_NAMESTORE_zone_iterator_next (list_it, 1);
988}
989
990
991/**
992 * Process a record that was stored in the namestore.
993 *
994 * @param cls closure
995 * @param zone_key private key of the zone
996 * @param rname name that is being mapped (at most 255 characters long)
997 * @param rd_len number of entries in @a rd array
998 * @param rd array of records with data to store
999 */
1000static void
1001display_record_monitor (void *cls,
1002 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
1003 const char *rname,
1004 unsigned int rd_len,
1005 const struct GNUNET_GNSRECORD_Data *rd,
1006 struct GNUNET_TIME_Absolute expiry)
1007{
1008 (void) cls;
1009 (void) zone_key;
1010 (void) expiry;
1011 display_record (zone_key, rname, rd_len, rd);
1012 GNUNET_NAMESTORE_zone_monitor_next (zm, 1);
1013}
1014
1015
1016/**
1017 * Process a record that was stored in the namestore.
1018 *
1019 * @param cls closure
1020 * @param zone_key private key of the zone
1021 * @param rname name that is being mapped (at most 255 characters long)
1022 * @param rd_len number of entries in @a rd array
1023 * @param rd array of records with data to store
1024 */
1025static void
1026display_record_lookup (void *cls,
1027 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
1028 const char *rname,
1029 unsigned int rd_len,
1030 const struct GNUNET_GNSRECORD_Data *rd)
1031{
1032 (void) cls;
1033 (void) zone_key;
1034 get_qe = NULL;
1035 display_record (zone_key, rname, rd_len, rd);
1036 finish_command ();
1037}
1038
1039
1040/**
1041 * Function called once we are in sync in monitor mode.
1042 *
1043 * @param cls NULL
1044 */
1045static void
1046sync_cb (void *cls)
1047{
1048 (void) cls;
1049 fprintf (stdout, "%s", "Monitor is now in sync.\n");
1050}
1051
1052
1053/**
1054 * Function called on errors while monitoring.
1055 *
1056 * @param cls NULL
1057 */
1058static void
1059monitor_error_cb (void *cls)
1060{
1061 (void) cls;
1062 fprintf (stderr, "%s", "Monitor disconnected and out of sync.\n");
1063}
1064
1065
1066/**
1067 * Function called on errors while monitoring.
1068 *
1069 * @param cls NULL
1070 */
1071static void
1072lookup_error_cb (void *cls)
1073{
1074 (void) cls;
1075 get_qe = NULL;
1076 fprintf (stderr, "%s", "Failed to lookup record.\n");
1077 finish_command ();
1078}
1079
1080
1081/**
1082 * Function called if lookup fails.
1083 */
1084static void
1085add_error_cb (void *cls)
1086{
1087 (void) cls;
1088 add_qe = NULL;
1089 GNUNET_break (0);
1090 ret = 1;
1091 finish_command ();
1092}
1093
1094
1095/**
1096 * We're storing a record; this function is given the existing record
1097 * so that we can merge the information.
1098 *
1099 * @param cls closure, unused
1100 * @param zone_key private key of the zone
1101 * @param rec_name name that is being mapped (at most 255 characters long)
1102 * @param rd_count number of entries in @a rd array
1103 * @param rd array of records with data to store
1104 */
1105static void
1106get_existing_record (void *cls,
1107 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
1108 const char *rec_name,
1109 unsigned int rd_count,
1110 const struct GNUNET_GNSRECORD_Data *rd)
1111{
1112 struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
1113 struct GNUNET_GNSRECORD_Data *rde;
1114
1115 (void) cls;
1116 (void) zone_key;
1117 add_qe = NULL;
1118 if (0 != strcmp (rec_name, name))
1119 {
1120 GNUNET_break (0);
1121 ret = 1;
1122 finish_command ();
1123 return;
1124 }
1125
1126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1127 "Received %u records for name `%s'\n",
1128 rd_count,
1129 rec_name);
1130 for (unsigned int i = 0; i < rd_count; i++)
1131 {
1132 switch (rd[i].record_type)
1133 {
1134 case GNUNET_DNSPARSER_TYPE_SOA:
1135 if (GNUNET_DNSPARSER_TYPE_SOA == type)
1136 {
1137 fprintf (
1138 stderr,
1139 _ (
1140 "A SOA record exists already under `%s', cannot add a second SOA to the same zone.\n"),
1141 rec_name);
1142 ret = 1;
1143 finish_command ();
1144 return;
1145 }
1146 break;
1147 }
1148 }
1149 memset (rdn, 0, sizeof(struct GNUNET_GNSRECORD_Data));
1150 GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof(struct GNUNET_GNSRECORD_Data));
1151 rde = &rdn[0];
1152 rde->data = data;
1153 rde->data_size = data_size;
1154 rde->record_type = type;
1155 if (1 == is_shadow)
1156 rde->flags |= GNUNET_GNSRECORD_RF_SHADOW;
1157 if (1 == is_maintenance)
1158 rde->flags |= GNUNET_GNSRECORD_RF_MAINTENANCE;
1159 if (1 != is_public)
1160 rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
1161 rde->expiration_time = etime;
1162 if (GNUNET_YES == etime_is_rel)
1163 rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1164 else if (GNUNET_NO != etime_is_rel)
1165 rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
1166 GNUNET_assert (NULL != name);
1167 add_qe = GNUNET_NAMESTORE_record_set_store (ns,
1168 &zone_pkey,
1169 name,
1170 rd_count + 1,
1171 rde,
1172 &add_continuation,
1173 &add_qe);
1174}
1175
1176
1177/**
1178 * Function called if we encountered an error in zone-to-name.
1179 */
1180static void
1181reverse_error_cb (void *cls)
1182{
1183 (void) cls;
1184 reverse_qe = NULL;
1185 fprintf (stdout, "%s.zkey\n", reverse_pkey);
1186}
1187
1188
1189/**
1190 * Function called with the result of our attempt to obtain a name for a given
1191 * public key.
1192 *
1193 * @param cls NULL
1194 * @param zone private key of the zone; NULL on disconnect
1195 * @param label label of the records; NULL on disconnect
1196 * @param rd_count number of entries in @a rd array, 0 if label was deleted
1197 * @param rd array of records with data to store
1198 */
1199static void
1200handle_reverse_lookup (void *cls,
1201 const struct GNUNET_CRYPTO_PrivateKey *zone,
1202 const char *label,
1203 unsigned int rd_count,
1204 const struct GNUNET_GNSRECORD_Data *rd)
1205{
1206 (void) cls;
1207 (void) zone;
1208 (void) rd_count;
1209 (void) rd;
1210 reverse_qe = NULL;
1211 if (NULL == label)
1212 fprintf (stdout, "%s\n", reverse_pkey);
1213 else
1214 fprintf (stdout, "%s.%s\n", label, ego_name);
1215 finish_command ();
1216}
1217
1218
1219/**
1220 * Function called if lookup for deletion fails.
1221 */
1222static void
1223del_lookup_error_cb (void *cls)
1224{
1225 (void) cls;
1226 del_qe = NULL;
1227 GNUNET_break (0);
1228 ret = 1;
1229 finish_command ();
1230}
1231
1232
1233/**
1234 * We were asked to delete something; this function is called with
1235 * the existing records. Now we should determine what should be
1236 * deleted and then issue the deletion operation.
1237 *
1238 * @param cls NULL
1239 * @param zone private key of the zone we are deleting from
1240 * @param label name of the records we are editing
1241 * @param rd_count size of the @a rd array
1242 * @param rd existing records
1243 */
1244static void
1245del_monitor (void *cls,
1246 const struct GNUNET_CRYPTO_PrivateKey *zone,
1247 const char *label,
1248 unsigned int rd_count,
1249 const struct GNUNET_GNSRECORD_Data *rd)
1250{
1251 struct GNUNET_GNSRECORD_Data rdx[rd_count];
1252 unsigned int rd_left;
1253 uint32_t type;
1254 char *vs;
1255
1256 (void) cls;
1257 (void) zone;
1258 del_qe = NULL;
1259 if (0 == rd_count)
1260 {
1261 fprintf (stderr,
1262 _ (
1263 "There are no records under label `%s' that could be deleted.\n"),
1264 label);
1265 ret = 1;
1266 finish_command ();
1267 return;
1268 }
1269 if ((NULL == value) && (NULL == typestring))
1270 {
1271 /* delete everything */
1272 del_qe = GNUNET_NAMESTORE_record_set_store (ns,
1273 &zone_pkey,
1274 name,
1275 0,
1276 NULL,
1277 &del_continuation,
1278 NULL);
1279 return;
1280 }
1281 rd_left = 0;
1282 if (NULL != typestring)
1283 type = GNUNET_GNSRECORD_typename_to_number (typestring);
1284 else
1285 type = GNUNET_GNSRECORD_TYPE_ANY;
1286 for (unsigned int i = 0; i < rd_count; i++)
1287 {
1288 vs = NULL;
1289 if (! (((GNUNET_GNSRECORD_TYPE_ANY == type) ||
1290 (rd[i].record_type == type)) &&
1291 ((NULL == value) ||
1292 (NULL ==
1293 (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
1294 rd[i].data,
1295 rd[i].data_size)))) ||
1296 (0 == strcmp (vs, value)))))
1297 rdx[rd_left++] = rd[i];
1298 GNUNET_free (vs);
1299 }
1300 if (rd_count == rd_left)
1301 {
1302 /* nothing got deleted */
1303 fprintf (
1304 stderr,
1305 _ (
1306 "There are no records under label `%s' that match the request for deletion.\n"),
1307 label);
1308 finish_command ();
1309 return;
1310 }
1311 /* delete everything but what we copied to 'rdx' */
1312 del_qe = GNUNET_NAMESTORE_record_set_store (ns,
1313 &zone_pkey,
1314 name,
1315 rd_left,
1316 rdx,
1317 &del_continuation,
1318 NULL);
1319}
1320
1321
1322static void
1323replace_cont (void *cls, enum GNUNET_ErrorCode ec)
1324{
1325 (void) cls;
1326
1327 set_qe = NULL;
1328 if (GNUNET_EC_NONE != ec)
1329 {
1330 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1331 _ ("%s\n"),
1332 GNUNET_ErrorCode_get_hint (ec));
1333 ret = 1; /* fail from 'main' */
1334 }
1335 finish_command ();
1336}
1337
1338
1339/**
1340 * We have obtained the zone's private key, so now process
1341 * the main commands using it.
1342 *
1343 * @param cfg configuration to use
1344 */
1345static void
1346run_with_zone_pkey (const struct GNUNET_CONFIGURATION_Handle *cfg)
1347{
1348 struct GNUNET_GNSRECORD_Data rd;
1349 enum GNUNET_GNSRECORD_Filter filter_flags = GNUNET_GNSRECORD_FILTER_NONE;
1350
1351 if (omit_private)
1352 filter_flags |= GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE;
1353 if (include_maintenance)
1354 filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE;
1355 if (! (add | del | list | (NULL != nickstring) | (NULL != uri)
1356 | (NULL != reverse_pkey) | (NULL != recordset) | (monitor)
1357 | (purge_orphaned) | (list_orphaned) | (purge_zone)) )
1358 {
1359 /* nothing more to be done */
1360 fprintf (stderr, _ ("No options given\n"));
1361 finish_command ();
1362 return;
1363 }
1364
1365 if (NULL != recordset)
1366 {
1367 /* replace entire record set */
1368 unsigned int rd_count;
1369 struct GNUNET_GNSRECORD_Data *rd;
1370
1371 /* FIXME: We could easily support append and delete with this as well */
1372 if (! add)
1373 {
1374 fprintf (stderr, _ ("Recordlines only work with option `%s'\n"),
1375 "-a");
1376 ret = 1;
1377 finish_command ();
1378 return;
1379 }
1380 if (NULL == name)
1381 {
1382 fprintf (stderr,
1383 _ ("Missing option `%s' for operation `%s'\n"),
1384 "-n",
1385 _ ("name"));
1386 ret = 1;
1387 finish_command ();
1388 return;
1389 }
1390 rd_count = 0;
1391 for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1392 rd_count++;
1393 rd = GNUNET_new_array (rd_count, struct GNUNET_GNSRECORD_Data);
1394 rd_count = 0;
1395 for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1396 {
1397 rd[rd_count] = e->record;
1398 rd_count++;
1399 }
1400 set_qe = GNUNET_NAMESTORE_record_set_store (ns,
1401 &zone_pkey,
1402 name,
1403 rd_count,
1404 rd,
1405 &replace_cont,
1406 NULL);
1407 GNUNET_free (rd);
1408 return;
1409 }
1410 if (NULL != nickstring)
1411 {
1412 if (0 == strlen (nickstring))
1413 {
1414 fprintf (stderr, _ ("Invalid nick `%s'\n"), nickstring);
1415 ret = 1;
1416 finish_command ();
1417 return;
1418 }
1419 add = 1;
1420 typestring = GNUNET_strdup (GNUNET_GNSRECORD_number_to_typename (
1421 GNUNET_GNSRECORD_TYPE_NICK));
1422 name = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT);
1423 value = GNUNET_strdup (nickstring);
1424 is_public = 0;
1425 expirationstring = GNUNET_strdup ("never");
1426 GNUNET_free (nickstring);
1427 nickstring = NULL;
1428 }
1429
1430 if (add)
1431 {
1432 if (NULL == ego_name)
1433 {
1434 fprintf (stderr,
1435 _ ("Missing option `%s' for operation `%s'\n"),
1436 "-z",
1437 _ ("add"));
1438 ret = 1;
1439 finish_command ();
1440 return;
1441 }
1442 if (NULL == name)
1443 {
1444 fprintf (stderr,
1445 _ ("Missing option `%s' for operation `%s'\n"),
1446 "-n",
1447 _ ("add"));
1448 ret = 1;
1449 finish_command ();
1450 return;
1451 }
1452 if (NULL == typestring)
1453 {
1454 fprintf (stderr,
1455 _ ("Missing option `%s' for operation `%s'\n"),
1456 "-t",
1457 _ ("add"));
1458 ret = 1;
1459 finish_command ();
1460 return;
1461 }
1462 type = GNUNET_GNSRECORD_typename_to_number (typestring);
1463 if (UINT32_MAX == type)
1464 {
1465 fprintf (stderr, _ ("Unsupported type `%s'\n"), typestring);
1466 ret = 1;
1467 finish_command ();
1468 return;
1469 }
1470 if ((GNUNET_DNSPARSER_TYPE_SRV == type) ||
1471 (GNUNET_DNSPARSER_TYPE_TLSA == type) ||
1472 (GNUNET_DNSPARSER_TYPE_SMIMEA == type) ||
1473 (GNUNET_DNSPARSER_TYPE_OPENPGPKEY == type))
1474 {
1475 fprintf (stderr,
1476 _ (
1477 "For DNS record types `SRV', `TLSA', `SMIMEA' and `OPENPGPKEY'"));
1478 fprintf (stderr, ", please use a `BOX' record instead\n");
1479 ret = 1;
1480 finish_command ();
1481 return;
1482 }
1483 if (NULL == value)
1484 {
1485 fprintf (stderr,
1486 _ ("Missing option `%s' for operation `%s'\n"),
1487 "-V",
1488 _ ("add"));
1489 ret = 1;
1490 finish_command ();
1491 return;
1492 }
1493 if (GNUNET_OK !=
1494 GNUNET_GNSRECORD_string_to_value (type, value, &data, &data_size))
1495 {
1496 fprintf (stderr,
1497 _ ("Value `%s' invalid for record type `%s'\n"),
1498 value,
1499 typestring);
1500 ret = 1;
1501 finish_command ();
1502 return;
1503 }
1504 if (NULL == expirationstring)
1505 {
1506 fprintf (stderr,
1507 _ ("Missing option `%s' for operation `%s'\n"),
1508 "-e",
1509 _ ("add"));
1510 ret = 1;
1511 finish_command ();
1512 return;
1513 }
1514 if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime))
1515 {
1516 fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring);
1517 ret = 1;
1518 finish_command ();
1519 return;
1520 }
1521 add_qe = GNUNET_NAMESTORE_records_lookup (ns,
1522 &zone_pkey,
1523 name,
1524 &add_error_cb,
1525 NULL,
1526 &get_existing_record,
1527 NULL);
1528 }
1529 if (del)
1530 {
1531 if (NULL == ego_name)
1532 {
1533 fprintf (stderr,
1534 _ ("Missing option `%s' for operation `%s'\n"),
1535 "-z",
1536 _ ("del"));
1537 ret = 1;
1538 finish_command ();
1539 return;
1540 }
1541 if (NULL == name)
1542 {
1543 fprintf (stderr,
1544 _ ("Missing option `%s' for operation `%s'\n"),
1545 "-n",
1546 _ ("del"));
1547 ret = 1;
1548 finish_command ();
1549 return;
1550 }
1551 del_qe = GNUNET_NAMESTORE_records_lookup2 (ns,
1552 &zone_pkey,
1553 name,
1554 &del_lookup_error_cb,
1555 NULL,
1556 &del_monitor,
1557 NULL,
1558 filter_flags);
1559 }
1560 if (purge_orphaned)
1561 {
1562 list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns,
1563 NULL,
1564 &zone_iteration_error_cb,
1565 NULL,
1566 &purge_orphans_iterator,
1567 NULL,
1568 &zone_iteration_finished,
1569 NULL,
1570 filter_flags);
1571
1572 }
1573 else if (purge_zone)
1574 {
1575 if (NULL == ego_name)
1576 {
1577 fprintf (stderr,
1578 _ ("Missing option `%s' for operation `%s'\n"),
1579 "-z",
1580 _ ("purge-zone"));
1581 ret = 1;
1582 finish_command ();
1583 return;
1584 }
1585 list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns,
1586 &zone_pkey,
1587 &zone_iteration_error_cb,
1588 NULL,
1589 &purge_zone_iterator,
1590 NULL,
1591 &zone_iteration_finished,
1592 NULL,
1593 filter_flags);
1594
1595 }
1596 else if (list || list_orphaned)
1597 {
1598 if (NULL != name)
1599 {
1600 if (NULL == ego_name)
1601 {
1602 fprintf (stderr,
1603 _ ("Missing option `%s' for operation `%s'\n"),
1604 "-z",
1605 _ ("list"));
1606 ret = 1;
1607 finish_command ();
1608 return;
1609 }
1610 get_qe = GNUNET_NAMESTORE_records_lookup (ns,
1611 &zone_pkey,
1612 name,
1613 &lookup_error_cb,
1614 NULL,
1615 &display_record_lookup,
1616 NULL);
1617 }
1618 else
1619 list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns,
1620 (NULL == ego_name) ?
1621 NULL : &zone_pkey,
1622 &zone_iteration_error_cb,
1623 NULL,
1624 &display_record_iterator,
1625 NULL,
1626 &zone_iteration_finished,
1627 NULL,
1628 filter_flags);
1629 }
1630 if (NULL != reverse_pkey)
1631 {
1632 struct GNUNET_CRYPTO_PublicKey pubkey;
1633
1634 if (NULL == ego_name)
1635 {
1636 fprintf (stderr,
1637 _ ("Missing option `%s' for operation `%s'\n"),
1638 "-z",
1639 _ ("reverse-pkey"));
1640 ret = 1;
1641 finish_command ();
1642 return;
1643 }
1644 if (GNUNET_OK !=
1645 GNUNET_CRYPTO_public_key_from_string (reverse_pkey,
1646 &pubkey))
1647 {
1648 fprintf (stderr,
1649 _ ("Invalid public key for reverse lookup `%s'\n"),
1650 reverse_pkey);
1651 ret = 1;
1652 finish_command ();
1653 return;
1654 }
1655 reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
1656 &zone_pkey,
1657 &pubkey,
1658 &reverse_error_cb,
1659 NULL,
1660 &handle_reverse_lookup,
1661 NULL);
1662 }
1663 if (NULL != uri)
1664 {
1665 char sh[105];
1666 char sname[64];
1667 struct GNUNET_CRYPTO_PublicKey pkey;
1668 if (NULL == ego_name)
1669 {
1670 fprintf (stderr,
1671 _ ("Missing option `%s' for operation `%s'\n"),
1672 "-z",
1673 _ ("uri"));
1674 ret = 1;
1675 finish_command ();
1676 return;
1677 }
1678
1679 memset (sh, 0, 105);
1680 memset (sname, 0, 64);
1681
1682 if ((2 != (sscanf (uri, "gnunet://gns/%58s/%63s", sh, sname))) ||
1683 (GNUNET_OK !=
1684 GNUNET_CRYPTO_public_key_from_string (sh, &pkey)))
1685 {
1686 fprintf (stderr, _ ("Invalid URI `%s'\n"), uri);
1687 ret = 1;
1688 finish_command ();
1689 return;
1690 }
1691 if (NULL == expirationstring)
1692 {
1693 fprintf (stderr,
1694 _ ("Missing option `%s' for operation `%s'\n"),
1695 "-e",
1696 _ ("add"));
1697 ret = 1;
1698 finish_command ();
1699 return;
1700 }
1701 if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime))
1702 {
1703 fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring);
1704 ret = 1;
1705 finish_command ();
1706 return;
1707 }
1708 memset (&rd, 0, sizeof(rd));
1709 rd.data = &pkey;
1710 rd.data_size = GNUNET_CRYPTO_public_key_get_length (&pkey);
1711 rd.record_type = ntohl (pkey.type);
1712 rd.expiration_time = etime;
1713 if (GNUNET_YES == etime_is_rel)
1714 rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1715 if (1 == is_shadow)
1716 rd.flags |= GNUNET_GNSRECORD_RF_SHADOW;
1717 if (1 == is_maintenance)
1718 rd.flags |= GNUNET_GNSRECORD_RF_MAINTENANCE;
1719 add_qe_uri = GNUNET_NAMESTORE_record_set_store (ns,
1720 &zone_pkey,
1721 sname,
1722 1,
1723 &rd,
1724 &add_continuation,
1725 &add_qe_uri);
1726 }
1727 if (monitor)
1728 {
1729 zm = GNUNET_NAMESTORE_zone_monitor_start2 (cfg,
1730 (NULL != ego_name) ?
1731 &zone_pkey : NULL,
1732 GNUNET_YES,
1733 &monitor_error_cb,
1734 NULL,
1735 &display_record_monitor,
1736 NULL,
1737 &sync_cb,
1738 NULL,
1739 filter_flags);
1740 }
1741}
1742
1743
1744#define MAX_LINE_LEN 4086
1745
1746#define MAX_ARGS 20
1747
1748static int
1749get_identity_for_string (const char *str,
1750 struct GNUNET_CRYPTO_PrivateKey *zk)
1751{
1752 const struct GNUNET_CRYPTO_PrivateKey *privkey;
1753 struct GNUNET_CRYPTO_PublicKey pubkey;
1754 struct GNUNET_CRYPTO_PublicKey ego_pubkey;
1755 struct EgoEntry *ego_entry;
1756
1757 if (GNUNET_OK == GNUNET_CRYPTO_public_key_from_string (str,
1758 &pubkey))
1759 {
1760 for (ego_entry = ego_head;
1761 NULL != ego_entry; ego_entry = ego_entry->next)
1762 {
1763 privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1764 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &ego_pubkey);
1765 if (0 == memcmp (&ego_pubkey, &pubkey, sizeof (pubkey)))
1766 {
1767 *zk = *privkey;
1768 return GNUNET_OK;
1769 }
1770 }
1771 }
1772 else
1773 {
1774 for (ego_entry = ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1775 {
1776 /** FIXME: Check for zTLD? **/
1777 if (0 != strcmp (str, ego_entry->identifier))
1778 continue;
1779 *zk = *GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1780 return GNUNET_OK;
1781 }
1782 }
1783 return GNUNET_NO;
1784}
1785
1786
1787static void
1788process_command_stdin ()
1789{
1790 char buf[MAX_LINE_LEN];
1791 static struct GNUNET_CRYPTO_PrivateKey next_zone_key;
1792 static char next_name[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
1793 static int finished = GNUNET_NO;
1794 static int have_next_zonekey = GNUNET_NO;
1795 int zonekey_set = GNUNET_NO;
1796 char *tmp;
1797
1798
1799 if (GNUNET_YES == have_next_zonekey)
1800 {
1801 zone_pkey = next_zone_key;
1802 if (NULL != name)
1803 GNUNET_free (name);
1804 name = GNUNET_strdup (next_name);
1805 zonekey_set = GNUNET_YES;
1806 }
1807 while (NULL != fgets (buf, sizeof (buf), stdin))
1808 {
1809 if (1 >= strlen (buf))
1810 continue;
1811 if (buf[strlen (buf) - 1] == '\n')
1812 buf[strlen (buf) - 1] = '\0';
1813 /**
1814 * Check if this is a new name. If yes, and we have records, store them.
1815 */
1816 if (buf[strlen (buf) - 1] == ':')
1817 {
1818 memset (next_name, 0, sizeof (next_name));
1819 strncpy (next_name, buf, strlen (buf) - 1);
1820 tmp = strchr (next_name, '.');
1821 if (NULL == tmp)
1822 {
1823 fprintf (stderr, "Error parsing name `%s'\n", next_name);
1824 GNUNET_SCHEDULER_shutdown ();
1825 ret = 1;
1826 return;
1827 }
1828 if (GNUNET_OK != get_identity_for_string (tmp + 1, &next_zone_key))
1829 {
1830 fprintf (stderr, "Error parsing zone name `%s'\n", tmp + 1);
1831 ret = 1;
1832 GNUNET_SCHEDULER_shutdown ();
1833 return;
1834 }
1835 *tmp = '\0';
1836 have_next_zonekey = GNUNET_YES;
1837 /* Run a command for the previous record set */
1838 if (NULL != recordset)
1839 {
1840 run_with_zone_pkey (cfg);
1841 return;
1842 }
1843 zone_pkey = next_zone_key;
1844 if (NULL != name)
1845 GNUNET_free (name);
1846 name = GNUNET_strdup (next_name);
1847 zonekey_set = GNUNET_YES;
1848 continue;
1849 }
1850 if (GNUNET_NO == zonekey_set)
1851 {
1852 fprintf (stderr, "Warning, encountered recordline without zone\n");
1853 continue;
1854 }
1855 parse_recordline (buf);
1856 }
1857 if (GNUNET_NO == finished)
1858 {
1859 if (NULL != recordset)
1860 {
1861 if (GNUNET_YES == zonekey_set)
1862 {
1863 run_with_zone_pkey (cfg); /** one last time **/
1864 finished = GNUNET_YES;
1865 return;
1866 }
1867 fprintf (stderr, "Warning, encountered recordline without zone\n");
1868 }
1869 }
1870 GNUNET_SCHEDULER_shutdown ();
1871 return;
1872}
1873
1874
1875/**
1876 * Function called with ALL of the egos known to the
1877 * identity service, used on startup if the user did
1878 * not specify a zone on the command-line.
1879 * Once the iteration is done (@a ego is NULL), we
1880 * ask for the default ego for "namestore".
1881 *
1882 * @param cls a `struct GNUNET_CONFIGURATION_Handle`
1883 * @param ego an ego, NULL for end of iteration
1884 * @param ctx NULL
1885 * @param name name associated with @a ego
1886 */
1887static void
1888id_connect_cb (void *cls,
1889 struct GNUNET_IDENTITY_Ego *ego,
1890 void **ctx,
1891 const char *name)
1892{
1893 struct GNUNET_CRYPTO_PublicKey pk;
1894 struct EgoEntry *ego_entry;
1895
1896 (void) ctx;
1897 (void) name;
1898 if ((NULL != name) && (NULL != ego))
1899 {
1900 ego_entry = GNUNET_new (struct EgoEntry);
1901 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1902 ego_entry->ego = ego;
1903 ego_entry->identifier = GNUNET_strdup (name);
1904 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1905 ego_tail,
1906 ego_entry);
1907 if ((NULL != ego_name) &&
1908 (0 == strcmp (name, ego_name)))
1909 zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1910 return;
1911 }
1912 if (NULL != ego)
1913 return;
1914 if (read_from_stdin)
1915 {
1916 process_command_stdin ();
1917 return;
1918 }
1919 run_with_zone_pkey (cfg);
1920}
1921
1922
1923/**
1924 * Main function that will be run.
1925 *
1926 * @param cls closure
1927 * @param args remaining command-line arguments
1928 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1929 * @param cfg configuration
1930 */
1931static void
1932run (void *cls,
1933 char *const *args,
1934 const char *cfgfile,
1935 const struct GNUNET_CONFIGURATION_Handle *_cfg)
1936{
1937 (void) cls;
1938 (void) args;
1939 (void) cfgfile;
1940 cfg = _cfg;
1941 if (NULL != args[0])
1942 GNUNET_log (
1943 GNUNET_ERROR_TYPE_WARNING,
1944 _ ("Superfluous command line arguments (starting with `%s') ignored\n"),
1945 args[0]);
1946
1947 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg);
1948 ns = GNUNET_NAMESTORE_connect (cfg);
1949 if (NULL == ns)
1950 {
1951 fprintf (stderr, _ ("Failed to connect to namestore\n"));
1952 GNUNET_SCHEDULER_shutdown ();
1953 return;
1954 }
1955 idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg);
1956 if (NULL == idh)
1957 {
1958 ret = -1;
1959 fprintf (stderr, _ ("Cannot connect to identity service\n"));
1960 GNUNET_SCHEDULER_shutdown ();
1961 }
1962}
1963
1964
1965/**
1966 * The main function for gnunet-namestore.
1967 *
1968 * @param argc number of arguments from the command line
1969 * @param argv command line arguments
1970 * @return 0 ok, 1 on error
1971 */
1972int
1973main (int argc, char *const *argv)
1974{
1975 int lret;
1976 struct GNUNET_GETOPT_CommandLineOption options[] =
1977 { GNUNET_GETOPT_option_flag ('a', "add", gettext_noop ("add record"), &add),
1978 GNUNET_GETOPT_option_flag ('d',
1979 "delete",
1980 gettext_noop ("delete record"),
1981 &del),
1982 GNUNET_GETOPT_option_flag ('D',
1983 "display",
1984 gettext_noop ("display records"),
1985 &list),
1986 GNUNET_GETOPT_option_flag ('S',
1987 "from-stdin",
1988 gettext_noop ("read commands from stdin"),
1989 &read_from_stdin),
1990 GNUNET_GETOPT_option_string (
1991 'e',
1992 "expiration",
1993 "TIME",
1994 gettext_noop (
1995 "expiration time for record to use (for adding only), \"never\" is possible"),
1996 &expirationstring),
1997 GNUNET_GETOPT_option_string ('i',
1998 "nick",
1999 "NICKNAME",
2000 gettext_noop (
2001 "set the desired nick name for the zone"),
2002 &nickstring),
2003 GNUNET_GETOPT_option_flag ('m',
2004 "monitor",
2005 gettext_noop (
2006 "monitor changes in the namestore"),
2007 &monitor),
2008 GNUNET_GETOPT_option_string ('n',
2009 "name",
2010 "NAME",
2011 gettext_noop (
2012 "name of the record to add/delete/display"),
2013 &name),
2014 GNUNET_GETOPT_option_flag ('r',
2015 "recordline",
2016 gettext_noop ("Output in recordline format"),
2017 &output_recordline),
2018 GNUNET_GETOPT_option_string ('Z',
2019 "zone-to-name",
2020 "KEY",
2021 gettext_noop (
2022 "determine our name for the given KEY"),
2023 &reverse_pkey),
2024 GNUNET_GETOPT_option_string ('t',
2025 "type",
2026 "TYPE",
2027 gettext_noop (
2028 "type of the record to add/delete/display"),
2029 &typestring),
2030 GNUNET_GETOPT_option_string ('u',
2031 "uri",
2032 "URI",
2033 gettext_noop ("URI to import into our zone"),
2034 &uri),
2035 GNUNET_GETOPT_option_string ('V',
2036 "value",
2037 "VALUE",
2038 gettext_noop (
2039 "value of the record to add/delete"),
2040 &value),
2041 GNUNET_GETOPT_option_flag ('p',
2042 "public",
2043 gettext_noop ("create or list public record"),
2044 &is_public),
2045 GNUNET_GETOPT_option_flag ('o',
2046 "omit-private",
2047 gettext_noop ("omit private records"),
2048 &omit_private),
2049 GNUNET_GETOPT_option_flag ('T',
2050 "include-maintenance",
2051 gettext_noop (
2052 "do not filter maintenance records"),
2053 &include_maintenance),
2054 GNUNET_GETOPT_option_flag ('P',
2055 "purge-orphans",
2056 gettext_noop (
2057 "purge namestore of all orphans"),
2058 &purge_orphaned),
2059 GNUNET_GETOPT_option_flag ('O',
2060 "list-orphans",
2061 gettext_noop (
2062 "show private key for orphaned records for recovery using `gnunet-identity -C -P <key>'. Use in combination with --display"),
2063 &list_orphaned),
2064 GNUNET_GETOPT_option_flag ('X',
2065 "purge-zone-records",
2066 gettext_noop (
2067 "delete all records in specified zone"),
2068 &purge_zone),
2069 GNUNET_GETOPT_option_flag (
2070 's',
2071 "shadow",
2072 gettext_noop (
2073 "create shadow record (only valid if all other records of the same type have expired)"),
2074 &is_shadow),
2075 GNUNET_GETOPT_option_flag (
2076 'M',
2077 "maintenance",
2078 gettext_noop (
2079 "create maintenance record (e.g TOMBSTONEs)"),
2080 &is_maintenance),
2081 GNUNET_GETOPT_option_string ('z',
2082 "zone",
2083 "EGO",
2084 gettext_noop (
2085 "name of the ego controlling the zone"),
2086 &ego_name),
2087 GNUNET_GETOPT_OPTION_END };
2088
2089
2090 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2091 return 2;
2092
2093 is_public = -1;
2094 is_shadow = -1;
2095 is_maintenance = -1;
2096 GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
2097 if (GNUNET_OK !=
2098 (lret = GNUNET_PROGRAM_run (argc,
2099 argv,
2100 "gnunet-namestore",
2101 _ ("GNUnet zone manipulation tool"),
2102 options,
2103 &run,
2104 NULL)))
2105 {
2106 GNUNET_free_nz ((void *) argv);
2107 // FIXME
2108 // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
2109 return lret;
2110 }
2111 GNUNET_free_nz ((void *) argv);
2112 // FIXME
2113 // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
2114 return ret;
2115}
2116
2117
2118/* end of gnunet-namestore.c */
diff --git a/src/cli/namestore/gnunet-zoneimport.c b/src/cli/namestore/gnunet-zoneimport.c
new file mode 100644
index 000000000..aaed808dd
--- /dev/null
+++ b/src/cli/namestore/gnunet-zoneimport.c
@@ -0,0 +1,1882 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file src/namestore/gnunet-zoneimport.c
22 * @brief import a DNS zone for publication in GNS, incremental
23 * @author Christian Grothoff
24 */
25#include <gnunet_util_lib.h>
26#include <gnunet_gnsrecord_lib.h>
27#include <gnunet_namestore_service.h>
28#include <gnunet_statistics_service.h>
29#include <gnunet_identity_service.h>
30
31
32/**
33 * Maximum number of queries pending at the same time.
34 */
35#define THRESH 100
36
37/**
38 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
39 * Used as an additional throttle.
40 */
41#define TIME_THRESH 10
42
43/**
44 * How often do we retry a query before giving up for good?
45 */
46#define MAX_RETRIES 5
47
48/**
49 * How many DNS requests do we at most issue in rapid series?
50 */
51#define MAX_SERIES 10
52
53/**
54 * How long do we wait at least between series of requests?
55 */
56#define SERIES_DELAY \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10)
58
59/**
60 * How long do DNS records have to last at least after being imported?
61 */
62static struct GNUNET_TIME_Relative minimum_expiration_time;
63
64/**
65 * How many requests do we request from NAMESTORE in one batch
66 * during our initial iteration?
67 */
68#define NS_BATCH_SIZE 1024
69
70/**
71 * Some zones may include authoritative records for other
72 * zones, such as foo.com.uk or bar.com.fr. As for GNS
73 * each dot represents a zone cut, we then need to create a
74 * zone on-the-fly to capture those records properly.
75 */
76struct Zone
77{
78 /**
79 * Kept in a DLL.
80 */
81 struct Zone *next;
82
83 /**
84 * Kept in a DLL.
85 */
86 struct Zone *prev;
87
88 /**
89 * Domain of the zone (i.e. "fr" or "com.fr")
90 */
91 char *domain;
92
93 /**
94 * Private key of the zone.
95 */
96 struct GNUNET_CRYPTO_PrivateKey key;
97};
98
99
100/**
101 * Record for the request to be stored by GNS.
102 */
103struct Record
104{
105 /**
106 * Kept in a DLL.
107 */
108 struct Record *next;
109
110 /**
111 * Kept in a DLL.
112 */
113 struct Record *prev;
114
115 /**
116 * GNS record.
117 */
118 struct GNUNET_GNSRECORD_Data grd;
119};
120
121
122/**
123 * Request we should make. We keep this struct in memory per request,
124 * thus optimizing it is crucial for the overall memory consumption of
125 * the zone importer.
126 */
127struct Request
128{
129 /**
130 * Requests are kept in a heap while waiting to be resolved.
131 */
132 struct GNUNET_CONTAINER_HeapNode *hn;
133
134 /**
135 * Active requests are kept in a DLL.
136 */
137 struct Request *next;
138
139 /**
140 * Active requests are kept in a DLL.
141 */
142 struct Request *prev;
143
144 /**
145 * Head of records that should be published in GNS for
146 * this hostname.
147 */
148 struct Record *rec_head;
149
150 /**
151 * Tail of records that should be published in GNS for
152 * this hostname.
153 */
154 struct Record *rec_tail;
155
156 /**
157 * Socket used to make the request, NULL if not active.
158 */
159 struct GNUNET_DNSSTUB_RequestSocket *rs;
160
161 /**
162 * Hostname we are resolving, allocated at the end of
163 * this struct (optimizing memory consumption by reducing
164 * total number of allocations).
165 */
166 char *hostname;
167
168 /**
169 * Namestore operation pending for this record.
170 */
171 struct GNUNET_NAMESTORE_QueueEntry *qe;
172
173 /**
174 * Zone responsible for this request.
175 */
176 const struct Zone *zone;
177
178 /**
179 * At what time does the (earliest) of the returned records
180 * for this name expire? At this point, we need to re-fetch
181 * the record.
182 */
183 struct GNUNET_TIME_Absolute expires;
184
185 /**
186 * While we are fetching the record, the value is set to the
187 * starting time of the DNS operation. While doing a
188 * NAMESTORE store, again set to the start time of the
189 * NAMESTORE operation.
190 */
191 struct GNUNET_TIME_Absolute op_start_time;
192
193 /**
194 * How often did we issue this query? (And failed, reset
195 * to zero once we were successful.)
196 */
197 unsigned int issue_num;
198
199 /**
200 * random 16-bit DNS query identifier.
201 */
202 uint16_t id;
203};
204
205
206/**
207 * Command-line argument specifying desired size of the hash map with
208 * all of our pending names. Usually, we use an automatically growing
209 * map, but this is only OK up to about a million entries. Above that
210 * number, the user must explicitly specify the size at startup.
211 */
212static unsigned int map_size = 1024;
213
214/**
215 * Handle to the identity service.
216 */
217static struct GNUNET_IDENTITY_Handle *id;
218
219/**
220 * Namestore handle.
221 */
222static struct GNUNET_NAMESTORE_Handle *ns;
223
224/**
225 * Handle to the statistics service.
226 */
227static struct GNUNET_STATISTICS_Handle *stats;
228
229/**
230 * Context for DNS resolution.
231 */
232static struct GNUNET_DNSSTUB_Context *ctx;
233
234/**
235 * The number of DNS queries that are outstanding
236 */
237static unsigned int pending;
238
239/**
240 * The number of NAMESTORE record store operations that are outstanding
241 */
242static unsigned int pending_rs;
243
244/**
245 * Number of lookups we performed overall.
246 */
247static unsigned int lookups;
248
249/**
250 * Number of records we had cached.
251 */
252static unsigned int cached;
253
254/**
255 * How many hostnames did we reject (malformed).
256 */
257static unsigned int rejects;
258
259/**
260 * Number of lookups that failed.
261 */
262static unsigned int failures;
263
264/**
265 * Number of records we found.
266 */
267static unsigned int records;
268
269/**
270 * Number of record sets given to namestore.
271 */
272static unsigned int record_sets;
273
274/**
275 * Heap of all requests to perform, sorted by
276 * the time we should next do the request (i.e. by expires).
277 */
278static struct GNUNET_CONTAINER_Heap *req_heap;
279
280/**
281 * Active requests are kept in a DLL.
282 */
283static struct Request *req_head;
284
285/**
286 * Active requests are kept in a DLL.
287 */
288static struct Request *req_tail;
289
290/**
291 * Main task.
292 */
293static struct GNUNET_SCHEDULER_Task *t;
294
295/**
296 * Hash map of requests for which we may still get a response from
297 * the namestore. Set to NULL once the initial namestore iteration
298 * is done.
299 */
300static struct GNUNET_CONTAINER_MultiHashMap *ns_pending;
301
302/**
303 * Current zone iteration handle.
304 */
305static struct GNUNET_NAMESTORE_ZoneIterator *zone_it;
306
307/**
308 * Head of list of zones we are managing.
309 */
310static struct Zone *zone_head;
311
312/**
313 * Tail of list of zones we are managing.
314 */
315static struct Zone *zone_tail;
316
317/**
318 * After how many more results must #ns_lookup_result_cb() ask
319 * the namestore for more?
320 */
321static uint64_t ns_iterator_trigger_next;
322
323/**
324 * Number of DNS requests counted in latency total.
325 */
326static uint64_t total_dns_latency_cnt;
327
328/**
329 * Sum of DNS latencies observed.
330 */
331static struct GNUNET_TIME_Relative total_dns_latency;
332
333/**
334 * Number of records processed (DNS lookup, no NAMESTORE) in total.
335 */
336static uint64_t total_reg_proc_dns;
337
338/**
339 * Number of records processed (DNS lookup, with NAMESTORE) in total.
340 */
341static uint64_t total_reg_proc_dns_ns;
342
343/**
344 * Start time of the regular processing.
345 */
346static struct GNUNET_TIME_Absolute start_time_reg_proc;
347
348/**
349 * Last time we worked before going idle.
350 */
351static struct GNUNET_TIME_Absolute sleep_time_reg_proc;
352
353/**
354 * Time we slept just waiting for work.
355 */
356static struct GNUNET_TIME_Relative idle_time;
357
358
359/**
360 * Callback for #for_all_records
361 *
362 * @param cls closure
363 * @param rec a DNS record
364 */
365typedef void (*RecordProcessor) (void *cls,
366 const struct GNUNET_DNSPARSER_Record *rec);
367
368
369/**
370 * Call @a rp for each record in @a p, regardless of
371 * what response section it is in.
372 *
373 * @param p packet from DNS
374 * @param rp function to call
375 * @param rp_cls closure for @a rp
376 */
377static void
378for_all_records (const struct GNUNET_DNSPARSER_Packet *p,
379 RecordProcessor rp,
380 void *rp_cls)
381{
382 for (unsigned int i = 0; i < p->num_answers; i++)
383 {
384 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
385
386 rp (rp_cls, rs);
387 }
388 for (unsigned int i = 0; i < p->num_authority_records; i++)
389 {
390 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
391
392 rp (rp_cls, rs);
393 }
394 for (unsigned int i = 0; i < p->num_additional_records; i++)
395 {
396 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
397
398 rp (rp_cls, rs);
399 }
400}
401
402
403/**
404 * Return just the label of the hostname in @a req.
405 *
406 * @param req request to process hostname of
407 * @return statically allocated pointer to the label,
408 * overwritten upon the next request!
409 */
410static const char *
411get_label (struct Request *req)
412{
413 static char label[64];
414 const char *dot;
415
416 dot = strchr (req->hostname, (unsigned char) '.');
417 if (NULL == dot)
418 {
419 GNUNET_break (0);
420 return NULL;
421 }
422 if (((size_t) (dot - req->hostname)) >= sizeof(label))
423 {
424 GNUNET_break (0);
425 return NULL;
426 }
427 GNUNET_memcpy (label, req->hostname, dot - req->hostname);
428 label[dot - req->hostname] = '\0';
429 return label;
430}
431
432
433/**
434 * Build DNS query for @a hostname.
435 *
436 * @param hostname host to build query for
437 * @param[out] raw_size number of bytes in the query
438 * @return NULL on error, otherwise pointer to statically (!)
439 * allocated query buffer
440 */
441static void *
442build_dns_query (struct Request *req, size_t *raw_size)
443{
444 static char raw[512];
445 char *rawp;
446 struct GNUNET_DNSPARSER_Packet p;
447 struct GNUNET_DNSPARSER_Query q;
448 int ret;
449
450 q.name = (char *) req->hostname;
451 q.type = GNUNET_DNSPARSER_TYPE_NS;
452 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
453
454 memset (&p, 0, sizeof(p));
455 p.num_queries = 1;
456 p.queries = &q;
457 p.id = req->id;
458 ret = GNUNET_DNSPARSER_pack (&p, UINT16_MAX, &rawp, raw_size);
459 if (GNUNET_OK != ret)
460 {
461 if (GNUNET_NO == ret)
462 GNUNET_free (rawp);
463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
464 "Failed to pack query for hostname `%s'\n",
465 req->hostname);
466 rejects++;
467 return NULL;
468 }
469 if (*raw_size > sizeof(raw))
470 {
471 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
472 "Failed to pack query for hostname `%s'\n",
473 req->hostname);
474 rejects++;
475 GNUNET_break (0);
476 GNUNET_free (rawp);
477 return NULL;
478 }
479 GNUNET_memcpy (raw, rawp, *raw_size);
480 GNUNET_free (rawp);
481 return raw;
482}
483
484
485/**
486 * Free records associated with @a req.
487 *
488 * @param req request to free records of
489 */
490static void
491free_records (struct Request *req)
492{
493 struct Record *rec;
494
495 /* Free records */
496 while (NULL != (rec = req->rec_head))
497 {
498 GNUNET_CONTAINER_DLL_remove (req->rec_head, req->rec_tail, rec);
499 GNUNET_free (rec);
500 }
501}
502
503
504/**
505 * Free @a req and data structures reachable from it.
506 *
507 * @param req request to free
508 */
509static void
510free_request (struct Request *req)
511{
512 free_records (req);
513 GNUNET_free (req);
514}
515
516
517/**
518 * Process as many requests as possible from the queue.
519 *
520 * @param cls NULL
521 */
522static void
523process_queue (void *cls);
524
525
526/**
527 * Insert @a req into DLL sorted by next fetch time.
528 *
529 * @param req request to insert into #req_heap
530 */
531static void
532insert_sorted (struct Request *req)
533{
534 req->hn =
535 GNUNET_CONTAINER_heap_insert (req_heap, req, req->expires.abs_value_us);
536 if (req == GNUNET_CONTAINER_heap_peek (req_heap))
537 {
538 if (NULL != t)
539 GNUNET_SCHEDULER_cancel (t);
540 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
541 t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL);
542 }
543}
544
545
546/**
547 * Add record to the GNS record set for @a req.
548 *
549 * @param req the request to expand GNS record set for
550 * @param type type to use
551 * @param expiration_time when should @a rec expire
552 * @param data raw data to store
553 * @param data_len number of bytes in @a data
554 */
555static void
556add_record (struct Request *req,
557 uint32_t type,
558 struct GNUNET_TIME_Absolute expiration_time,
559 const void *data,
560 size_t data_len)
561{
562 struct Record *rec;
563
564 rec = GNUNET_malloc (sizeof(struct Record) + data_len);
565 rec->grd.data = &rec[1];
566 rec->grd.expiration_time = expiration_time.abs_value_us;
567 rec->grd.data_size = data_len;
568 rec->grd.record_type = type;
569 rec->grd.flags = GNUNET_GNSRECORD_RF_NONE;
570 GNUNET_memcpy (&rec[1], data, data_len);
571 GNUNET_CONTAINER_DLL_insert (req->rec_head, req->rec_tail, rec);
572}
573
574
575/**
576 * Closure for #check_for_glue.
577 */
578struct GlueClosure
579{
580 /**
581 * Overall request we are processing.
582 */
583 struct Request *req;
584
585 /**
586 * NS name we are looking for glue for.
587 */
588 const char *ns;
589
590 /**
591 * Set to #GNUNET_YES if glue was found.
592 */
593 int found;
594};
595
596
597/**
598 * Try to find glue records for a given NS record.
599 *
600 * @param cls a `struct GlueClosure *`
601 * @param rec record that may contain glue information
602 */
603static void
604check_for_glue (void *cls, const struct GNUNET_DNSPARSER_Record *rec)
605{
606 struct GlueClosure *gc = cls;
607 char dst[65536];
608 size_t dst_len;
609 size_t off;
610 char ip[INET6_ADDRSTRLEN + 1];
611 socklen_t ip_size = (socklen_t) sizeof(ip);
612 struct GNUNET_TIME_Absolute expiration_time;
613 struct GNUNET_TIME_Relative left;
614
615 if (0 != strcasecmp (rec->name, gc->ns))
616 return;
617 expiration_time = rec->expiration_time;
618 left = GNUNET_TIME_absolute_get_remaining (expiration_time);
619 if (0 == left.rel_value_us)
620 return; /* ignore expired glue records */
621 /* if expiration window is too short, bump it to configured minimum */
622 if (left.rel_value_us < minimum_expiration_time.rel_value_us)
623 expiration_time =
624 GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
625 dst_len = sizeof(dst);
626 off = 0;
627 switch (rec->type)
628 {
629 case GNUNET_DNSPARSER_TYPE_A:
630 if (sizeof(struct in_addr) != rec->data.raw.data_len)
631 {
632 GNUNET_break (0);
633 return;
634 }
635 if (NULL == inet_ntop (AF_INET, rec->data.raw.data, ip, ip_size))
636 {
637 GNUNET_break (0);
638 return;
639 }
640 if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
641 dst_len,
642 &off,
643 gc->req->hostname)) &&
644 (GNUNET_OK ==
645 GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip)))
646 {
647 add_record (gc->req,
648 GNUNET_GNSRECORD_TYPE_GNS2DNS,
649 expiration_time,
650 dst,
651 off);
652 gc->found = GNUNET_YES;
653 }
654 break;
655
656 case GNUNET_DNSPARSER_TYPE_AAAA:
657 if (sizeof(struct in6_addr) != rec->data.raw.data_len)
658 {
659 GNUNET_break (0);
660 return;
661 }
662 if (NULL == inet_ntop (AF_INET6, rec->data.raw.data, ip, ip_size))
663 {
664 GNUNET_break (0);
665 return;
666 }
667 if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
668 dst_len,
669 &off,
670 gc->req->hostname)) &&
671 (GNUNET_OK ==
672 GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip)))
673 {
674 add_record (gc->req,
675 GNUNET_GNSRECORD_TYPE_GNS2DNS,
676 expiration_time,
677 dst,
678 off);
679 gc->found = GNUNET_YES;
680 }
681 break;
682
683 case GNUNET_DNSPARSER_TYPE_CNAME:
684 if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
685 dst_len,
686 &off,
687 gc->req->hostname)) &&
688 (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
689 dst_len,
690 &off,
691 rec->data.hostname)))
692 {
693 add_record (gc->req,
694 GNUNET_GNSRECORD_TYPE_GNS2DNS,
695 expiration_time,
696 dst,
697 off);
698 gc->found = GNUNET_YES;
699 }
700 break;
701
702 default:
703 /* useless, do nothing */
704 break;
705 }
706}
707
708
709/**
710 * Closure for #process_record().
711 */
712struct ProcessRecordContext
713{
714 /**
715 * Answer we got back and are currently parsing, or NULL
716 * if not active.
717 */
718 struct GNUNET_DNSPARSER_Packet *p;
719
720 /**
721 * Request we are processing.
722 */
723 struct Request *req;
724};
725
726
727/**
728 * We received @a rec for @a req. Remember the answer.
729 *
730 * @param cls a `struct ProcessRecordContext`
731 * @param rec response
732 */
733static void
734process_record (void *cls, const struct GNUNET_DNSPARSER_Record *rec)
735{
736 struct ProcessRecordContext *prc = cls;
737 struct Request *req = prc->req;
738 char dst[65536];
739 size_t dst_len;
740 size_t off;
741 struct GNUNET_TIME_Absolute expiration_time;
742 struct GNUNET_TIME_Relative left;
743
744 dst_len = sizeof(dst);
745 off = 0;
746 records++;
747 if (0 != strcasecmp (rec->name, req->hostname))
748 {
749 GNUNET_log (
750 GNUNET_ERROR_TYPE_DEBUG,
751 "DNS returned record from zone `%s' of type %u while resolving `%s'\n",
752 rec->name,
753 (unsigned int) rec->type,
754 req->hostname);
755 return; /* does not match hostname, might be glue, but
756 not useful for this pass! */
757 }
758 expiration_time = rec->expiration_time;
759 left = GNUNET_TIME_absolute_get_remaining (expiration_time);
760 if (0 == left.rel_value_us)
761 {
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
763 "DNS returned expired record for `%s'\n",
764 req->hostname);
765 GNUNET_STATISTICS_update (stats,
766 "# expired records obtained from DNS",
767 1,
768 GNUNET_NO);
769 return; /* record expired */
770 }
771
772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
773 "DNS returned record that expires at %s for `%s'\n",
774 GNUNET_STRINGS_absolute_time_to_string (expiration_time),
775 req->hostname);
776 /* if expiration window is too short, bump it to configured minimum */
777 if (left.rel_value_us < minimum_expiration_time.rel_value_us)
778 expiration_time =
779 GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
780 switch (rec->type)
781 {
782 case GNUNET_DNSPARSER_TYPE_NS: {
783 struct GlueClosure gc;
784
785 /* check for glue */
786 gc.req = req;
787 gc.ns = rec->data.hostname;
788 gc.found = GNUNET_NO;
789 for_all_records (prc->p, &check_for_glue, &gc);
790 if ((GNUNET_NO == gc.found) &&
791 (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
792 dst_len,
793 &off,
794 req->hostname)) &&
795 (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
796 dst_len,
797 &off,
798 rec->data.hostname)))
799 {
800 /* FIXME: actually check if this is out-of-bailiwick,
801 and if not request explicit resolution... */
802 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
803 "Converted OOB (`%s') NS record for `%s'\n",
804 rec->data.hostname,
805 rec->name);
806 add_record (req,
807 GNUNET_GNSRECORD_TYPE_GNS2DNS,
808 expiration_time,
809 dst,
810 off);
811 }
812 else
813 {
814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815 "Converted NS record for `%s' using glue\n",
816 rec->name);
817 }
818 break;
819 }
820
821 case GNUNET_DNSPARSER_TYPE_CNAME:
822 if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
823 dst_len,
824 &off,
825 rec->data.hostname))
826 {
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "Converting CNAME (`%s') record for `%s'\n",
829 rec->data.hostname,
830 rec->name);
831 add_record (req, rec->type, expiration_time, dst, off);
832 }
833 break;
834
835 case GNUNET_DNSPARSER_TYPE_DNAME:
836 /* No support for DNAME in GNS yet! FIXME: support later! */
837 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
838 "FIXME: not supported: %s DNAME %s\n",
839 rec->name,
840 rec->data.hostname);
841 break;
842
843 case GNUNET_DNSPARSER_TYPE_MX:
844 if (GNUNET_OK ==
845 GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &off, rec->data.mx))
846 {
847 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
848 "Converting MX (`%s') record for `%s'\n",
849 rec->data.mx->mxhost,
850 rec->name);
851 add_record (req, rec->type, expiration_time, dst, off);
852 }
853 break;
854
855 case GNUNET_DNSPARSER_TYPE_SOA:
856 if (GNUNET_OK ==
857 GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &off, rec->data.soa))
858 {
859 /* NOTE: GNS does not really use SOAs */
860 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
861 "Converting SOA record for `%s'\n",
862 rec->name);
863 add_record (req, rec->type, expiration_time, dst, off);
864 }
865 break;
866
867 case GNUNET_DNSPARSER_TYPE_SRV:
868 if (GNUNET_OK ==
869 GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &off, rec->data.srv))
870 {
871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872 "Converting SRV record for `%s'\n",
873 rec->name);
874 add_record (req, rec->type, expiration_time, dst, off);
875 }
876 break;
877
878 case GNUNET_DNSPARSER_TYPE_URI:
879 if (GNUNET_OK ==
880 GNUNET_DNSPARSER_builder_add_uri (dst, dst_len, &off, rec->data.uri))
881 {
882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
883 "Converting URI record for `%s'\n",
884 rec->name);
885 add_record (req, rec->type, expiration_time, dst, off);
886 }
887 break;
888
889 case GNUNET_DNSPARSER_TYPE_PTR:
890 if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
891 dst_len,
892 &off,
893 rec->data.hostname))
894 {
895 /* !?: what does a PTR record do in a regular TLD??? */
896 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897 "Converting PTR record for `%s' (weird)\n",
898 rec->name);
899 add_record (req, rec->type, expiration_time, dst, off);
900 }
901 break;
902
903 case GNUNET_DNSPARSER_TYPE_CERT:
904 if (GNUNET_OK ==
905 GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &off, rec->data.cert))
906 {
907 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
908 "Converting CERT record for `%s'\n",
909 rec->name);
910 add_record (req, rec->type, expiration_time, dst, off);
911 }
912 break;
913
914 /* Rest is 'raw' encoded and just needs to be copied IF
915 the hostname matches the requested name; otherwise we
916 simply cannot use it. */
917 case GNUNET_DNSPARSER_TYPE_A:
918 case GNUNET_DNSPARSER_TYPE_AAAA:
919 case GNUNET_DNSPARSER_TYPE_TXT:
920 default:
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922 "Converting record of type %u for `%s'\n",
923 (unsigned int) rec->type,
924 rec->name);
925 add_record (req,
926 rec->type,
927 expiration_time,
928 rec->data.raw.data,
929 rec->data.raw.data_len);
930 break;
931 }
932}
933
934
935static void
936store_completed_cb (void *cls, enum GNUNET_ErrorCode ec)
937{
938 static struct GNUNET_TIME_Absolute last;
939 struct Request *req = cls;
940
941 req->qe = NULL;
942 if (GNUNET_EC_NONE != ec)
943 {
944 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
945 "Failed to store zone data for `%s': %s\n",
946 req->hostname,
947 GNUNET_ErrorCode_get_hint (ec));
948 }
949 else
950 {
951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
952 "Stored records under `%s' (%d)\n",
953 req->hostname,
954 ec);
955 }
956 total_reg_proc_dns_ns++; /* finished regular processing */
957 pending_rs--;
958 free_records (req);
959 /* compute NAMESTORE statistics */
960 {
961 static uint64_t total_ns_latency_cnt;
962 static struct GNUNET_TIME_Relative total_ns_latency;
963 struct GNUNET_TIME_Relative ns_latency;
964
965 ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
966 total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency, ns_latency);
967 if (0 == total_ns_latency_cnt)
968 last = GNUNET_TIME_absolute_get ();
969 total_ns_latency_cnt++;
970 if (0 == (total_ns_latency_cnt % 1000))
971 {
972 struct GNUNET_TIME_Relative delta;
973
974 delta = GNUNET_TIME_absolute_get_duration (last);
975 last = GNUNET_TIME_absolute_get ();
976 fprintf (stderr,
977 "Processed 1000 records in %s\n",
978 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
979 GNUNET_STATISTICS_set (stats,
980 "# average NAMESTORE PUT latency (μs)",
981 total_ns_latency.rel_value_us
982 / total_ns_latency_cnt,
983 GNUNET_NO);
984 }
985 }
986 /* compute and publish overall velocity */
987 if (0 == (total_reg_proc_dns_ns % 100))
988 {
989 struct GNUNET_TIME_Relative runtime;
990
991 runtime = GNUNET_TIME_absolute_get_duration (start_time_reg_proc);
992 runtime = GNUNET_TIME_relative_subtract (runtime, idle_time);
993 runtime =
994 GNUNET_TIME_relative_divide (runtime,
995 total_reg_proc_dns + total_reg_proc_dns_ns);
996 GNUNET_STATISTICS_set (stats,
997 "# Regular processing completed without NAMESTORE",
998 total_reg_proc_dns,
999 GNUNET_NO);
1000 GNUNET_STATISTICS_set (stats,
1001 "# Regular processing completed with NAMESTORE PUT",
1002 total_reg_proc_dns_ns,
1003 GNUNET_NO);
1004 GNUNET_STATISTICS_set (stats,
1005 "# average request processing latency (μs)",
1006 runtime.rel_value_us,
1007 GNUNET_NO);
1008 GNUNET_STATISTICS_set (stats,
1009 "# total time spent idle (μs)",
1010 idle_time.rel_value_us,
1011 GNUNET_NO);
1012 }
1013
1014 if (NULL == t)
1015 {
1016 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1017 t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1018 }
1019}
1020
1021
1022/**
1023 * Function called with the result of a DNS resolution.
1024 *
1025 * @param cls closure with the `struct Request`
1026 * @param dns dns response, never NULL
1027 * @param dns_len number of bytes in @a dns
1028 */
1029static void
1030process_result (void *cls,
1031 const struct GNUNET_TUN_DnsHeader *dns,
1032 size_t dns_len)
1033{
1034 struct Request *req = cls;
1035 struct Record *rec;
1036 struct GNUNET_DNSPARSER_Packet *p;
1037 unsigned int rd_count;
1038
1039 GNUNET_assert (NULL == req->hn);
1040 if (NULL == dns)
1041 {
1042 /* stub gave up */
1043 GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1044 pending--;
1045 if (NULL == t)
1046 {
1047 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1048 t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1049 }
1050 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1051 "Stub gave up on DNS reply for `%s'\n",
1052 req->hostname);
1053 GNUNET_STATISTICS_update (stats, "# DNS lookups timed out", 1, GNUNET_NO);
1054 if (req->issue_num > MAX_RETRIES)
1055 {
1056 failures++;
1057 free_request (req);
1058 GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO);
1059 return;
1060 }
1061 total_reg_proc_dns++;
1062 req->rs = NULL;
1063 insert_sorted (req);
1064 return;
1065 }
1066 if (req->id != dns->id)
1067 {
1068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069 "DNS ID did not match request, ignoring reply\n");
1070 GNUNET_STATISTICS_update (stats, "# DNS ID mismatches", 1, GNUNET_NO);
1071 return;
1072 }
1073 GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1074 GNUNET_DNSSTUB_resolve_cancel (req->rs);
1075 req->rs = NULL;
1076 pending--;
1077 p = GNUNET_DNSPARSER_parse ((const char *) dns, dns_len);
1078 if (NULL == p)
1079 {
1080 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1081 "Failed to parse DNS reply for `%s'\n",
1082 req->hostname);
1083 GNUNET_STATISTICS_update (stats, "# DNS parser errors", 1, GNUNET_NO);
1084 if (NULL == t)
1085 {
1086 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1087 t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1088 }
1089 if (req->issue_num > MAX_RETRIES)
1090 {
1091 failures++;
1092 free_request (req);
1093 GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO);
1094 return;
1095 }
1096 insert_sorted (req);
1097 return;
1098 }
1099 /* import new records */
1100 req->issue_num = 0; /* success, reset counter! */
1101 {
1102 struct ProcessRecordContext prc = { .req = req, .p = p };
1103
1104 for_all_records (p, &process_record, &prc);
1105 }
1106 GNUNET_DNSPARSER_free_packet (p);
1107 /* count records found, determine minimum expiration time */
1108 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1109 {
1110 struct GNUNET_TIME_Relative dns_latency;
1111
1112 dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
1113 total_dns_latency =
1114 GNUNET_TIME_relative_add (total_dns_latency, dns_latency);
1115 total_dns_latency_cnt++;
1116 if (0 == (total_dns_latency_cnt % 1000))
1117 {
1118 GNUNET_STATISTICS_set (stats,
1119 "# average DNS lookup latency (μs)",
1120 total_dns_latency.rel_value_us
1121 / total_dns_latency_cnt,
1122 GNUNET_NO);
1123 }
1124 }
1125 rd_count = 0;
1126 for (rec = req->rec_head; NULL != rec; rec = rec->next)
1127 {
1128 struct GNUNET_TIME_Absolute at;
1129
1130 at.abs_value_us = rec->grd.expiration_time;
1131 req->expires = GNUNET_TIME_absolute_min (req->expires, at);
1132 rd_count++;
1133 }
1134 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1135 "Obtained %u records for `%s'\n",
1136 rd_count,
1137 req->hostname);
1138 /* Instead of going for SOA, simplified for now to look each
1139 day in case we got an empty response */
1140 if (0 == rd_count)
1141 {
1142 req->expires = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
1143 GNUNET_STATISTICS_update (stats,
1144 "# empty DNS replies (usually NXDOMAIN)",
1145 1,
1146 GNUNET_NO);
1147 }
1148 else
1149 {
1150 record_sets++;
1151 }
1152 /* convert records to namestore import format */
1153 {
1154 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
1155 unsigned int off = 0;
1156
1157 /* convert linked list into array */
1158 for (rec = req->rec_head; NULL != rec; rec = rec->next)
1159 rd[off++] = rec->grd;
1160 pending_rs++;
1161 req->op_start_time = GNUNET_TIME_absolute_get ();
1162 req->qe = GNUNET_NAMESTORE_record_set_store (ns,
1163 &req->zone->key,
1164 get_label (req),
1165 rd_count,
1166 rd,
1167 &store_completed_cb,
1168 req);
1169 GNUNET_assert (NULL != req->qe);
1170 }
1171 insert_sorted (req);
1172}
1173
1174
1175/**
1176 * Process as many requests as possible from the queue.
1177 *
1178 * @param cls NULL
1179 */
1180static void
1181process_queue (void *cls)
1182{
1183 struct Request *req;
1184 unsigned int series;
1185 void *raw;
1186 size_t raw_size;
1187 struct GNUNET_TIME_Relative delay;
1188
1189 (void) cls;
1190 delay = GNUNET_TIME_absolute_get_duration (sleep_time_reg_proc);
1191 idle_time = GNUNET_TIME_relative_add (idle_time, delay);
1192 series = 0;
1193 t = NULL;
1194 while (pending + pending_rs < THRESH)
1195 {
1196 req = GNUNET_CONTAINER_heap_peek (req_heap);
1197 if (NULL == req)
1198 break;
1199 if (NULL != req->qe)
1200 return; /* namestore op still pending */
1201 if (NULL != req->rs)
1202 {
1203 GNUNET_break (0);
1204 return; /* already submitted */
1205 }
1206 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1207 break;
1208 GNUNET_assert (req == GNUNET_CONTAINER_heap_remove_root (req_heap));
1209 req->hn = NULL;
1210 GNUNET_CONTAINER_DLL_insert (req_head, req_tail, req);
1211 GNUNET_assert (NULL == req->rs);
1212 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1213 "Requesting resolution for `%s'\n",
1214 req->hostname);
1215 raw = build_dns_query (req, &raw_size);
1216 if (NULL == raw)
1217 {
1218 GNUNET_break (0);
1219 free_request (req);
1220 continue;
1221 }
1222 req->op_start_time = GNUNET_TIME_absolute_get ();
1223 req->rs = GNUNET_DNSSTUB_resolve (ctx, raw, raw_size, &process_result, req);
1224 GNUNET_assert (NULL != req->rs);
1225 req->issue_num++;
1226 lookups++;
1227 pending++;
1228 series++;
1229 if (series > MAX_SERIES)
1230 break;
1231 }
1232 if (pending + pending_rs >= THRESH)
1233 {
1234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1235 "Stopped processing queue (%u+%u/%u)]\n",
1236 pending,
1237 pending_rs,
1238 THRESH);
1239 return; /* wait for replies */
1240 }
1241 req = GNUNET_CONTAINER_heap_peek (req_heap);
1242 if (NULL == req)
1243 {
1244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1245 "Stopped processing queue: empty queue\n");
1246 return;
1247 }
1248 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1249 {
1250 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1251 "Waiting until %s for next record (`%s') to expire\n",
1252 GNUNET_STRINGS_absolute_time_to_string (req->expires),
1253 req->hostname);
1254 if (NULL != t)
1255 GNUNET_SCHEDULER_cancel (t);
1256 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1257 t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL);
1258 return;
1259 }
1260 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Throttling\n");
1261 if (NULL != t)
1262 GNUNET_SCHEDULER_cancel (t);
1263 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1264 t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY, &process_queue, NULL);
1265}
1266
1267
1268/**
1269 * Iterator called during #do_shutdown() to free requests in
1270 * the #ns_pending map.
1271 *
1272 * @param cls NULL
1273 * @param key unused
1274 * @param value the `struct Request` to free
1275 * @return #GNUNET_OK
1276 */
1277static int
1278free_request_it (void *cls, const struct GNUNET_HashCode *key, void *value)
1279{
1280 struct Request *req = value;
1281
1282 (void) cls;
1283 (void) key;
1284 free_request (req);
1285 return GNUNET_OK;
1286}
1287
1288
1289/**
1290 * Clean up and terminate the process.
1291 *
1292 * @param cls NULL
1293 */
1294static void
1295do_shutdown (void *cls)
1296{
1297 struct Request *req;
1298 struct Zone *zone;
1299
1300 (void) cls;
1301 if (NULL != id)
1302 {
1303 GNUNET_IDENTITY_disconnect (id);
1304 id = NULL;
1305 }
1306 if (NULL != t)
1307 {
1308 GNUNET_SCHEDULER_cancel (t);
1309 t = NULL;
1310 }
1311 while (NULL != (req = req_head))
1312 {
1313 GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1314 if (NULL != req->qe)
1315 GNUNET_NAMESTORE_cancel (req->qe);
1316 free_request (req);
1317 }
1318 while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap)))
1319 {
1320 req->hn = NULL;
1321 if (NULL != req->qe)
1322 GNUNET_NAMESTORE_cancel (req->qe);
1323 free_request (req);
1324 }
1325 if (NULL != zone_it)
1326 {
1327 GNUNET_NAMESTORE_zone_iteration_stop (zone_it);
1328 zone_it = NULL;
1329 }
1330 if (NULL != ns)
1331 {
1332 GNUNET_NAMESTORE_disconnect (ns);
1333 ns = NULL;
1334 }
1335 if (NULL != ctx)
1336 {
1337 GNUNET_DNSSTUB_stop (ctx);
1338 ctx = NULL;
1339 }
1340 if (NULL != req_heap)
1341 {
1342 GNUNET_CONTAINER_heap_destroy (req_heap);
1343 req_heap = NULL;
1344 }
1345 if (NULL != ns_pending)
1346 {
1347 GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &free_request_it, NULL);
1348 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1349 ns_pending = NULL;
1350 }
1351 while (NULL != (zone = zone_head))
1352 {
1353 GNUNET_CONTAINER_DLL_remove (zone_head, zone_tail, zone);
1354 GNUNET_free (zone->domain);
1355 GNUNET_free (zone);
1356 }
1357 if (NULL != stats)
1358 {
1359 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1360 stats = NULL;
1361 }
1362}
1363
1364
1365/**
1366 * Iterate over all of the zones we care about and see which records
1367 * we may need to re-fetch when.
1368 *
1369 * @param cls NULL
1370 */
1371static void
1372iterate_zones (void *cls);
1373
1374
1375/**
1376 * Function called if #GNUNET_NAMESTORE_records_lookup() failed.
1377 * Just logs an error.
1378 *
1379 * @param cls a `struct Zone`
1380 */
1381static void
1382ns_lookup_error_cb (void *cls)
1383{
1384 struct Zone *zone = cls;
1385
1386 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1387 "Failed to load data from namestore for zone `%s'\n",
1388 zone->domain);
1389 zone_it = NULL;
1390 ns_iterator_trigger_next = 0;
1391 iterate_zones (NULL);
1392}
1393
1394
1395/**
1396 * Process a record that was stored in the namestore.
1397 *
1398 * @param cls a `struct Zone *`
1399 * @param key private key of the zone
1400 * @param label label of the records
1401 * @param rd_count number of entries in @a rd array, 0 if label was deleted
1402 * @param rd array of records with data to store
1403 */
1404static void
1405ns_lookup_result_cb (void *cls,
1406 const struct GNUNET_CRYPTO_PrivateKey *key,
1407 const char *label,
1408 unsigned int rd_count,
1409 const struct GNUNET_GNSRECORD_Data *rd)
1410{
1411 struct Zone *zone = cls;
1412 struct Request *req;
1413 struct GNUNET_HashCode hc;
1414 char *fqdn;
1415
1416 ns_iterator_trigger_next--;
1417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418 "Obtained NAMESTORE reply, %llu left in round\n",
1419 (unsigned long long) ns_iterator_trigger_next);
1420 if (0 == ns_iterator_trigger_next)
1421 {
1422 ns_iterator_trigger_next = NS_BATCH_SIZE;
1423 GNUNET_STATISTICS_update (stats,
1424 "# NAMESTORE records requested from cache",
1425 ns_iterator_trigger_next,
1426 GNUNET_NO);
1427 GNUNET_NAMESTORE_zone_iterator_next (zone_it, ns_iterator_trigger_next);
1428 }
1429 GNUNET_asprintf (&fqdn, "%s.%s", label, zone->domain);
1430 GNUNET_CRYPTO_hash (fqdn, strlen (fqdn) + 1, &hc);
1431 GNUNET_free (fqdn);
1432 req = GNUNET_CONTAINER_multihashmap_get (ns_pending, &hc);
1433 if (NULL == req)
1434 {
1435 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1436 "Ignoring record `%s' in zone `%s': not on my list!\n",
1437 label,
1438 zone->domain);
1439 return;
1440 }
1441 GNUNET_assert (GNUNET_OK ==
1442 GNUNET_CONTAINER_multihashmap_remove (ns_pending, &hc, req));
1443 GNUNET_break (0 == GNUNET_memcmp (key, &req->zone->key));
1444 GNUNET_break (0 == strcasecmp (label, get_label (req)));
1445 for (unsigned int i = 0; i < rd_count; i++)
1446 {
1447 struct GNUNET_TIME_Absolute at;
1448
1449 if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
1450 {
1451 struct GNUNET_TIME_Relative rel;
1452
1453 rel.rel_value_us = rd->expiration_time;
1454 at = GNUNET_TIME_relative_to_absolute (rel);
1455 }
1456 else
1457 {
1458 at.abs_value_us = rd->expiration_time;
1459 }
1460 add_record (req, rd->record_type, at, rd->data, rd->data_size);
1461 }
1462 if (0 == rd_count)
1463 {
1464 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1465 "Empty record set in namestore for `%s'\n",
1466 req->hostname);
1467 }
1468 else
1469 {
1470 unsigned int pos = 0;
1471
1472 cached++;
1473 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1474 for (struct Record *rec = req->rec_head; NULL != rec; rec = rec->next)
1475 {
1476 struct GNUNET_TIME_Absolute at;
1477
1478 at.abs_value_us = rec->grd.expiration_time;
1479 req->expires = GNUNET_TIME_absolute_min (req->expires, at);
1480 pos++;
1481 }
1482 if (0 == pos)
1483 req->expires = GNUNET_TIME_UNIT_ZERO_ABS;
1484 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1485 "Hot-start with %u existing records for `%s'\n",
1486 pos,
1487 req->hostname);
1488 }
1489 free_records (req);
1490
1491 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1492 "Adding `%s' to worklist to start at %s\n",
1493 req->hostname,
1494 GNUNET_STRINGS_absolute_time_to_string (req->expires));
1495 insert_sorted (req);
1496}
1497
1498
1499/**
1500 * Add @a hostname to the list of requests to be made.
1501 *
1502 * @param hostname name to resolve
1503 */
1504static void
1505queue (const char *hostname)
1506{
1507 struct Request *req;
1508 const char *dot;
1509 struct Zone *zone;
1510 size_t hlen;
1511 struct GNUNET_HashCode hc;
1512
1513 if (GNUNET_OK != GNUNET_DNSPARSER_check_name (hostname))
1514 {
1515 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1516 "Refusing invalid hostname `%s'\n",
1517 hostname);
1518 rejects++;
1519 return;
1520 }
1521 dot = strchr (hostname, (unsigned char) '.');
1522 if (NULL == dot)
1523 {
1524 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1525 "Refusing invalid hostname `%s' (lacks '.')\n",
1526 hostname);
1527 rejects++;
1528 return;
1529 }
1530 for (zone = zone_head; NULL != zone; zone = zone->next)
1531 if (0 == strcmp (zone->domain, dot + 1))
1532 break;
1533 if (NULL == zone)
1534 {
1535 rejects++;
1536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1537 "Domain name `%s' not in ego list!\n",
1538 dot + 1);
1539 return;
1540 }
1541
1542 hlen = strlen (hostname) + 1;
1543 req = GNUNET_malloc (sizeof(struct Request) + hlen);
1544 req->zone = zone;
1545 req->hostname = (char *) &req[1];
1546 GNUNET_memcpy (req->hostname, hostname, hlen);
1547 req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1548 UINT16_MAX);
1549 GNUNET_CRYPTO_hash (req->hostname, hlen, &hc);
1550 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
1551 ns_pending,
1552 &hc,
1553 req,
1554 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1555 {
1556 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1557 "Duplicate hostname `%s' ignored\n",
1558 hostname);
1559 GNUNET_free (req);
1560 return;
1561 }
1562}
1563
1564
1565/**
1566 * We have completed the initial iteration over the namestore's database.
1567 * This function is called on each of the remaining records in
1568 * #move_to_queue to #queue() them, as we will simply not find existing
1569 * records for them any longer.
1570 *
1571 * @param cls NULL
1572 * @param key unused
1573 * @param value a `struct Request`
1574 * @return #GNUNET_OK (continue to iterate)
1575 */
1576static int
1577move_to_queue (void *cls, const struct GNUNET_HashCode *key, void *value)
1578{
1579 struct Request *req = value;
1580
1581 (void) cls;
1582 (void) key;
1583 insert_sorted (req);
1584 return GNUNET_OK;
1585}
1586
1587
1588/**
1589 * Iterate over all of the zones we care about and see which records
1590 * we may need to re-fetch when.
1591 *
1592 * @param cls NULL
1593 */
1594static void
1595iterate_zones (void *cls)
1596{
1597 static struct Zone *last;
1598
1599 (void) cls;
1600 if (NULL != zone_it)
1601 {
1602 zone_it = NULL;
1603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1604 "Finished iteration over zone `%s'!\n",
1605 last->domain);
1606 /* subtract left-overs from previous iteration */
1607 GNUNET_STATISTICS_update (stats,
1608 "# NAMESTORE records requested from cache",
1609 (long long) (-ns_iterator_trigger_next),
1610 GNUNET_NO);
1611 ns_iterator_trigger_next = 0;
1612 }
1613 GNUNET_assert (NULL != zone_tail);
1614 if (zone_tail == last)
1615 {
1616 /* Done iterating over relevant zones in NAMESTORE, move
1617 rest of hash map to work queue as well. */
1618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1619 "Finished all NAMESTORE iterations!\n");
1620 GNUNET_STATISTICS_set (stats,
1621 "# Domain names without cached reply",
1622 GNUNET_CONTAINER_multihashmap_size (ns_pending),
1623 GNUNET_NO);
1624 GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &move_to_queue, NULL);
1625 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1626 ns_pending = NULL;
1627 start_time_reg_proc = GNUNET_TIME_absolute_get ();
1628 total_reg_proc_dns = 0;
1629 total_reg_proc_dns_ns = 0;
1630 return;
1631 }
1632 if (NULL == last)
1633 last = zone_head;
1634 else
1635 last = last->next;
1636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1637 "Starting iteration over zone `%s'!\n",
1638 last->domain);
1639 /* subtract left-overs from previous iteration */
1640 GNUNET_STATISTICS_update (stats,
1641 "# NAMESTORE records requested from cache",
1642 1,
1643 GNUNET_NO);
1644 ns_iterator_trigger_next = 1;
1645 GNUNET_STATISTICS_update (stats, "# zones iterated", 1, GNUNET_NO);
1646 zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1647 &last->key,
1648 &ns_lookup_error_cb,
1649 NULL,
1650 &ns_lookup_result_cb,
1651 last,
1652 &iterate_zones,
1653 NULL);
1654}
1655
1656
1657/**
1658 * Begin processing hostnames from stdin.
1659 *
1660 * @param cls NULL
1661 */
1662static void
1663process_stdin (void *cls)
1664{
1665 static struct GNUNET_TIME_Absolute last;
1666 static uint64_t idot;
1667 char hn[256];
1668
1669 (void) cls;
1670 t = NULL;
1671 if (NULL != id)
1672 {
1673 GNUNET_IDENTITY_disconnect (id);
1674 id = NULL;
1675 }
1676 while (NULL != fgets (hn, sizeof(hn), stdin))
1677 {
1678 if (strlen (hn) > 0)
1679 hn[strlen (hn) - 1] = '\0'; /* eat newline */
1680 if (0 == idot)
1681 last = GNUNET_TIME_absolute_get ();
1682 idot++;
1683 if (0 == idot % 100000)
1684 {
1685 struct GNUNET_TIME_Relative delta;
1686
1687 delta = GNUNET_TIME_absolute_get_duration (last);
1688 last = GNUNET_TIME_absolute_get ();
1689 fprintf (stderr,
1690 "Read 100000 domain names in %s\n",
1691 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
1692 GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO);
1693 }
1694 queue (hn);
1695 }
1696 fprintf (stderr,
1697 "Done reading %llu domain names\n",
1698 (unsigned long long) idot);
1699 GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO);
1700 iterate_zones (NULL);
1701}
1702
1703
1704/**
1705 * Method called to inform about the egos of this peer.
1706 *
1707 * When used with #GNUNET_IDENTITY_connect, this function is
1708 * initially called for all egos and then again whenever a
1709 * ego's name changes or if it is deleted. At the end of
1710 * the initial pass over all egos, the function is once called
1711 * with 'NULL' for @a ego. That does NOT mean that the callback won't
1712 * be invoked in the future or that there was an error.
1713 *
1714 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this
1715 * function is only called ONCE, and 'NULL' being passed in @a ego does
1716 * indicate an error (for example because name is taken or no default value is
1717 * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the
1718 * value WILL be passed to a subsequent call to the identity callback of
1719 * #GNUNET_IDENTITY_connect (if that one was not NULL).
1720 *
1721 * When an identity is renamed, this function is called with the
1722 * (known) @a ego but the NEW @a name.
1723 *
1724 * When an identity is deleted, this function is called with the
1725 * (known) ego and "NULL" for the @a name. In this case,
1726 * the @a ego is henceforth invalid (and the @a ctx should also be
1727 * cleaned up).
1728 *
1729 * @param cls closure
1730 * @param ego ego handle, NULL for end of list
1731 * @param ctx context for application to store data for this ego
1732 * (during the lifetime of this process, initially NULL)
1733 * @param name name assigned by the user for this ego,
1734 * NULL if the user just deleted the ego and it
1735 * must thus no longer be used
1736 */
1737static void
1738identity_cb (void *cls,
1739 struct GNUNET_IDENTITY_Ego *ego,
1740 void **ctx,
1741 const char *name)
1742{
1743 (void) cls;
1744 (void) ctx;
1745
1746 if (NULL == ego)
1747 {
1748 /* end of iteration */
1749 if (NULL == zone_head)
1750 {
1751 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No zone found\n");
1752 GNUNET_SCHEDULER_shutdown ();
1753 return;
1754 }
1755 /* zone_head non-null, process hostnames from stdin */
1756 t = GNUNET_SCHEDULER_add_now (&process_stdin, NULL);
1757 return;
1758 }
1759 if (NULL != name)
1760 {
1761 struct Zone *zone;
1762
1763 zone = GNUNET_new (struct Zone);
1764 zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1765 zone->domain = GNUNET_strdup (name);
1766 GNUNET_CONTAINER_DLL_insert (zone_head, zone_tail, zone);
1767 }
1768}
1769
1770
1771/**
1772 * Process requests from the queue, then if the queue is
1773 * not empty, try again.
1774 *
1775 * @param cls NULL
1776 * @param args remaining command-line arguments
1777 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1778 * @param cfg configuration
1779 */
1780static void
1781run (void *cls,
1782 char *const *args,
1783 const char *cfgfile,
1784 const struct GNUNET_CONFIGURATION_Handle *cfg)
1785{
1786 (void) cls;
1787 (void) args;
1788 (void) cfgfile;
1789 stats = GNUNET_STATISTICS_create ("zoneimport", cfg);
1790 req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1791 ns_pending = GNUNET_CONTAINER_multihashmap_create (map_size, GNUNET_NO);
1792 if (NULL == ns_pending)
1793 {
1794 fprintf (stderr, "Failed to allocate memory for main hash map\n");
1795 return;
1796 }
1797 ctx = GNUNET_DNSSTUB_start (256);
1798 if (NULL == ctx)
1799 {
1800 fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n");
1801 return;
1802 }
1803 if (NULL == args[0])
1804 {
1805 fprintf (stderr,
1806 "You must provide a list of DNS resolvers on the command line\n");
1807 return;
1808 }
1809 for (unsigned int i = 0; NULL != args[i]; i++)
1810 {
1811 if (GNUNET_OK != GNUNET_DNSSTUB_add_dns_ip (ctx, args[i]))
1812 {
1813 fprintf (stderr, "Failed to use `%s' for DNS resolver\n", args[i]);
1814 return;
1815 }
1816 }
1817
1818
1819 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1820 ns = GNUNET_NAMESTORE_connect (cfg);
1821 if (NULL == ns)
1822 {
1823 GNUNET_SCHEDULER_shutdown ();
1824 return;
1825 }
1826 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1827}
1828
1829
1830/**
1831 * Call with IP address of resolver to query.
1832 *
1833 * @param argc should be 2
1834 * @param argv[1] should contain IP address
1835 * @return 0 on success
1836 */
1837int
1838main (int argc, char *const *argv)
1839{
1840 struct GNUNET_GETOPT_CommandLineOption options[] =
1841 { GNUNET_GETOPT_option_uint ('s',
1842 "size",
1843 "MAPSIZE",
1844 gettext_noop (
1845 "size to use for the main hash map"),
1846 &map_size),
1847 GNUNET_GETOPT_option_relative_time (
1848 'm',
1849 "minimum-expiration",
1850 "RELATIVETIME",
1851 gettext_noop ("minimum expiration time we assume for imported records"),
1852 &minimum_expiration_time),
1853 GNUNET_GETOPT_OPTION_END };
1854 int ret;
1855
1856 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1857 return 2;
1858 if (GNUNET_OK != (ret = GNUNET_PROGRAM_run (argc,
1859 argv,
1860 "gnunet-zoneimport",
1861 "import DNS zone into namestore",
1862 options,
1863 &run,
1864 NULL)))
1865 return ret;
1866 GNUNET_free_nz ((void *) argv);
1867 fprintf (stderr,
1868 "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n"
1869 "Found %u records, %u lookups failed, %u/%u pending on shutdown\n",
1870 rejects,
1871 cached,
1872 lookups,
1873 record_sets,
1874 records,
1875 failures,
1876 pending,
1877 pending_rs);
1878 return 0;
1879}
1880
1881
1882/* end of gnunet-zoneimport.c */
diff --git a/src/cli/namestore/meson.build b/src/cli/namestore/meson.build
new file mode 100644
index 000000000..e619876c5
--- /dev/null
+++ b/src/cli/namestore/meson.build
@@ -0,0 +1,38 @@
1executable ('gnunet-namestore',
2 ['gnunet-namestore.c'],
3 dependencies: [libgnunetnamestore_dep,
4 libgnunetutil_dep,
5 libgnunetgnsrecord_dep,
6 libgnunetidentity_dep],
7 include_directories: [incdir, configuration_inc],
8 install: true,
9 install_dir: get_option('bindir'))
10executable ('gnunet-namestore-dbtool',
11 ['gnunet-namestore-dbtool.c'],
12 dependencies: [libgnunetnamestore_dep,
13 libgnunetutil_dep,
14 libgnunetgnsrecord_dep,
15 libgnunetidentity_dep],
16 include_directories: [incdir, configuration_inc],
17 install: true,
18 install_dir: get_option('bindir'))
19executable ('gnunet-namestore-zonefile',
20 ['gnunet-namestore-zonefile.c'],
21 dependencies: [libgnunetnamestore_dep,
22 libgnunetutil_dep,
23 libgnunetgnsrecord_dep,
24 libgnunetidentity_dep],
25 include_directories: [incdir, configuration_inc],
26 install: true,
27 install_dir: get_option('bindir'))
28executable ('gnunet-zoneimport',
29 ['gnunet-zoneimport.c'],
30 dependencies: [libgnunetnamestore_dep,
31 libgnunetutil_dep,
32 libgnunetstatistics_dep,
33 libgnunetgnsrecord_dep,
34 libgnunetidentity_dep],
35 include_directories: [incdir, configuration_inc],
36 install: true,
37 install_dir: get_option('bindir'))
38
diff --git a/src/cli/namestore/test_namestore_box_lightest.sh b/src/cli/namestore/test_namestore_box_lightest.sh
new file mode 100755
index 000000000..3c8ad2799
--- /dev/null
+++ b/src/cli/namestore/test_namestore_box_lightest.sh
@@ -0,0 +1,63 @@
1#!/bin/bash
2CONFIGURATION="test_namestore_api.conf"
3trap "gnunet-arm -e -c $CONFIGURATION" SIGINT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME`
18TEST_RECORD_NAME_DNS="trust"
19TEST_RECORD_VALUE_SMIMEA="49152 49153 53 0 0 1 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971"
20TEST_RECORD_VALUE_URI="49152 49152 256 10 10 \"http://lightest.nletlabs.nl/\""
21which timeout &> /dev/null && DO_TIMEOUT="timeout 5"
22
23function start_peer
24{
25 gnunet-arm -s -c $CONFIGURATION
26 gnunet-identity -C testego -c $CONFIGURATION
27}
28
29function stop_peer
30{
31 gnunet-identity -D testego -c $CONFIGURATION
32 gnunet-arm -e -c $CONFIGURATION
33}
34
35
36start_peer
37# Create a public SMIMEA record
38gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t BOX -V "$TEST_RECORD_VALUE_SMIMEA" -e never -c $CONFIGURATION
39NAMESTORE_RES=$?
40
41if [ $NAMESTORE_RES = 0 ]
42then
43 echo "PASS: Creating boxed name in namestore SMIMEA"
44else
45 echo "FAIL: Creating boxed name in namestore failed with $NAMESTORE_RES."
46 stop_peer
47 exit 1
48fi
49
50# Create a public URI record
51gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t BOX -V "$TEST_RECORD_VALUE_URI" -e never -c $CONFIGURATION
52NAMESTORE_RES=$?
53
54if [ $NAMESTORE_RES = 0 ]
55then
56 echo "PASS: Creating boxed name in namestore URI"
57else
58 echo "FAIL: Creating boxed name in namestore failed with $NAMESTORE_RES."
59 stop_peer
60 exit 1
61fi
62
63stop_peer \ No newline at end of file
diff --git a/src/cli/namestore/test_namestore_delete.sh b/src/cli/namestore/test_namestore_delete.sh
new file mode 100755
index 000000000..b861a4bc0
--- /dev/null
+++ b/src/cli/namestore/test_namestore_delete.sh
@@ -0,0 +1,68 @@
1#!/bin/bash
2CONFIGURATION="test_namestore_api.conf"
3trap "gnunet-arm -e -c $CONFIGURATION" SIGINT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME`
18TEST_DOMAIN_PLUS="www.gnu"
19TEST_DOMAIN_DNS="www3.gnu"
20TEST_IP_PLUS="127.0.0.1"
21TEST_IP_DNS="131.159.74.67"
22TEST_RECORD_CNAME_SERVER="server"
23TEST_RECORD_CNAME_PLUS="server.+"
24TEST_RECORD_CNAME_DNS="gnunet.org"
25TEST_RECORD_NAME_SERVER="server"
26TEST_RECORD_NAME_PLUS="www"
27TEST_RECORD_NAME_DNS="www3"
28which timeout &> /dev/null && DO_TIMEOUT="timeout 5"
29
30function start_peer
31{
32 gnunet-arm -s -c $CONFIGURATION
33 gnunet-identity -C testego -c $CONFIGURATION
34}
35
36function stop_peer
37{
38 gnunet-identity -D testego -c $CONFIGURATION
39 gnunet-arm -e -c $CONFIGURATION
40}
41
42
43start_peer
44# Create a public record
45gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION
46# Delete record
47gnunet-namestore -p -z testego -d -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION
48# List all records
49OUTPUT=`gnunet-namestore -p -z testego -D`
50FOUND_IP=false
51FOUND_NAME=false
52for LINE in $OUTPUT ;
53 do
54 if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then
55 FOUND_NAME=true;
56 fi
57 if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then
58 FOUND_IP=true;
59 fi
60 done
61stop_peer
62
63
64if [ $FOUND_IP = true ]
65then
66 echo "FAIL: Delete name in namestore: IP returned"
67 exit 1
68fi
diff --git a/src/cli/namestore/test_namestore_lookup.sh b/src/cli/namestore/test_namestore_lookup.sh
new file mode 100755
index 000000000..1c96e102a
--- /dev/null
+++ b/src/cli/namestore/test_namestore_lookup.sh
@@ -0,0 +1,63 @@
1#!/bin/bash
2CONFIGURATION="test_namestore_api.conf"
3trap "gnunet-arm -e -c $CONFIGURATION" SIGINT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME`
18TEST_IP_PLUS="127.0.0.1"
19TEST_RECORD_NAME_DNS="www3"
20which timeout &> /dev/null && DO_TIMEOUT="timeout 5"
21
22# start peer
23gnunet-arm -s -c $CONFIGURATION
24gnunet-identity -C testego -c $CONFIGURATION
25
26# Create a public record
27gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION
28NAMESTORE_RES=$?
29# Lookup specific name
30OUTPUT=`gnunet-namestore -p -z testego -n $TEST_RECORD_NAME_DNS -D`
31
32
33FOUND_IP=false
34FOUND_NAME=false
35for LINE in $OUTPUT ;
36 do
37 if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then
38 FOUND_NAME=true;
39 #echo $FOUND_NAME
40 fi
41 if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then
42 FOUND_IP=true;
43 #echo $FOUND_IP
44 fi
45done
46# stop peer
47gnunet-identity -D testego -c $CONFIGURATION
48gnunet-arm -e -c $CONFIGURATION
49
50
51if [ $FOUND_NAME = true -a $FOUND_IP = true ]
52then
53 echo "PASS: Lookup name in namestore"
54 exit 0
55elif [ $FOUND_NAME = false ]
56then
57 echo "FAIL: Lookup name in namestore: name not returned"
58 exit 1
59elif [ $FOUND_IP = false ]
60then
61 echo "FAIL: Lookup name in namestore: IP not returned"
62 exit 1
63fi
diff --git a/src/cli/namestore/test_namestore_put.sh b/src/cli/namestore/test_namestore_put.sh
new file mode 100755
index 000000000..eaf7d44b4
--- /dev/null
+++ b/src/cli/namestore/test_namestore_put.sh
@@ -0,0 +1,55 @@
1#!/bin/bash
2CONFIGURATION="test_namestore_api.conf"
3trap "gnunet-arm -e -c $CONFIGURATION" SIGINT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME`
18TEST_DOMAIN_PLUS="www.gnu"
19TEST_DOMAIN_DNS="www3.gnu"
20TEST_IP_PLUS="127.0.0.1"
21TEST_IP_DNS="131.159.74.67"
22TEST_RECORD_CNAME_SERVER="server"
23TEST_RECORD_CNAME_PLUS="server.+"
24TEST_RECORD_CNAME_DNS="gnunet.org"
25TEST_RECORD_NAME_SERVER="server"
26TEST_RECORD_NAME_PLUS="www"
27TEST_RECORD_NAME_DNS="www3"
28which timeout &> /dev/null && DO_TIMEOUT="timeout 5"
29
30function start_peer
31{
32 gnunet-arm -s -c $CONFIGURATION
33 gnunet-identity -C testego -c $CONFIGURATION
34}
35
36function stop_peer
37{
38 gnunet-identity -D testego -c $CONFIGURATION
39 gnunet-arm -e -c $CONFIGURATION
40}
41
42
43start_peer
44# Create a public record
45gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION
46NAMESTORE_RES=$?
47stop_peer
48
49if [ $NAMESTORE_RES = 0 ]
50then
51 echo "PASS: Creating name in namestore"
52else
53 echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES."
54 exit 1
55fi
diff --git a/src/cli/namestore/test_namestore_put_multiple.sh b/src/cli/namestore/test_namestore_put_multiple.sh
new file mode 100755
index 000000000..4c7340440
--- /dev/null
+++ b/src/cli/namestore/test_namestore_put_multiple.sh
@@ -0,0 +1,112 @@
1#!/bin/bash
2
3# Check for required packages
4if ! [ -x "$(command -v gnunet-namestore)" ]; then
5 echo 'bind/named is not installed' >&2
6 exit 1
7fi
8
9# Check if gnunet is running
10gnunet-arm -I 2&>1 /dev/null
11ret=$?
12if [ 0 -ne $ret ]; then
13 echo 'gnunet services are not running'
14 exit 1
15fi
16
17## GNUNET part
18# Check if identity exists and deletes and readds it to get rid of entries in zone
19gnunet-identity -d | grep randomtestingid 2>&1 /dev/null
20ret=$?
21
22if [ 0 -ne $ret ]; then
23 gnunet-identity -D randomtestingid
24 gnunet-identity -C randomtestingid
25fi
26
27function get_record_type {
28 arr=$1
29 typ=$(echo -n "${arr[0]}" | cut -d' ' -f1)
30 echo "$typ"
31}
32
33function get_value {
34 arr=$1
35 val=$(echo -n "${arr[0]}" | cut -d' ' -f4-)
36 echo "$val"
37}
38
39function testing {
40 label=$1
41 records=$2
42 recordstring=""
43 typ=$(get_record_type "${records[@]}")
44 for i in "${records[@]}"
45 do
46 recordstring+="$i"$'\n'
47 done
48 echo "$recordstring"
49 gnunet-namestore -a -S <<EOF
50$label.randomtestingid:
51 $recordstring
52EOF
53 ret=$?
54 if [ 0 -ne $ret ]; then
55 echo "failed to add record $label: $recordstring"
56 fi
57 gnunet-gns -t "$typ" -u foo2.randomtestingid 2>&1 /dev/null
58 if [ 0 -ne $ret ]; then
59 echo "record $label could not be found"
60 fi
61}
62
63# TEST CASES
64# 1
65echo "Testing adding of single A record with -R"
66declare -a arr=('A 1200 [r] 127.0.0.1')
67testing test1 "${arr[@]}"
68# 2
69echo "Testing adding of multiple A records with -R"
70declare -a arr=('A 1200 [r] 127.0.0.1' 'A 2400 [r] 127.0.0.2')
71testing test2 "${arr[@]}"
72# 3
73echo "Testing adding of multiple different records with -R"
74declare -a arr=('A 1200 [r] 127.0.0.1' 'AAAA 2400 [r] 2002::')
75testing test3 "${arr[@]}"
76# 4
77echo "Testing adding of single GNS2DNS record with -R"
78declare -a arr=('GNS2DNS 86400 [r] gnu.org@127.0.0.1')
79testing test4 "${arr[@]}"
80# 5
81echo "Testing adding of single GNS2DNS shadow record with -R"
82declare -a arr=('GNS2DNS 86409 [rs] gnu.org@127.0.0.250')
83testing test5 "${arr[@]}"
84# 6
85echo "Testing adding of multiple GNS2DNS record with -R"
86declare -a arr=('GNS2DNS 1 [r] gnunet.org@127.0.0.1' 'GNS2DNS 3600 [s] gnunet.org@127.0.0.2')
87testing test6 "${arr[@]}"
88val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid)
89if [[ $val == *"127.0.0.1"* ]]; then
90 echo "shadow!"
91fi
92echo "Sleeping to let record expire"
93sleep 5
94val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid)
95if [[ $val == *"127.0.0.2"* ]]; then
96 echo "no shadow!"
97fi
98# 7
99echo "Testing adding MX record with -R"
100declare -a arr=('MX 3600 [r] 10,mail')
101testing test7 "${arr[@]}"
102# 8
103echo "Testing adding TXT record with -R"
104declare -a arr=('TXT 3600 [r] Pretty_Unicorns')
105testing test8 "${arr[@]}"
106# 8
107#echo "Testing adding TXT record with -R"
108#declare -a arr=('SRV 3600 [r] _autodiscover_old._tcp.bfh.ch.')
109#testing test8 "${arr[@]}"
110
111# CLEANUP
112gnunet-identity -D randomtestingid
diff --git a/src/cli/namestore/test_namestore_put_stdin.sh b/src/cli/namestore/test_namestore_put_stdin.sh
new file mode 100755
index 000000000..deb7bc4cb
--- /dev/null
+++ b/src/cli/namestore/test_namestore_put_stdin.sh
@@ -0,0 +1,68 @@
1#!/bin/bash
2CONFIGURATION="test_namestore_api.conf"
3trap "gnunet-arm -e -c $CONFIGURATION" SIGINT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME`
18TEST_RECORD_NAME="www3"
19TEST_RECORD_NAME2="www"
20TEST_IP="8.7.6.5"
21TEST_IP2="1.2.3.4"
22
23which timeout &> /dev/null && DO_TIMEOUT="timeout 5"
24
25function start_peer
26{
27 gnunet-arm -s -c $CONFIGURATION
28 gnunet-identity -C testego -c $CONFIGURATION
29 gnunet-identity -C testego2 -c $CONFIGURATION
30}
31
32function stop_peer
33{
34 gnunet-identity -D testego -c $CONFIGURATION
35 gnunet-identity -D testego2 -c $CONFIGURATION
36 gnunet-arm -e -c $CONFIGURATION
37}
38
39
40start_peer
41# Create a public record
42EGOKEY=`gnunet-identity -d | grep testego2 | cut -d' ' -f3`
43gnunet-namestore -a -c $CONFIGURATION -S <<EOF
44$TEST_RECORD_NAME.testego:
45 A 3600000000 [pr] $TEST_IP
46 TXT 21438201833 [r] $TEST_IP2
47
48 TXT 21438201833 [r] aslkdj asdlkjaslkd 232!
49
50$TEST_RECORD_NAME2.testego:
51 AAAA 324241223 [prS] ::dead:beef
52 A 111324241223000000 [pC] 1.1.1.1
53
54www7.$EGOKEY:
55 A 3600000000 [pr] $TEST_IP
56
57EOF
58NAMESTORE_RES=$?
59gnunet-namestore -D -r -c $CONFIGURATION
60stop_peer
61
62if [ $NAMESTORE_RES = 0 ]
63then
64 echo "PASS: Creating name in namestore"
65else
66 echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES."
67 exit 1
68fi
diff --git a/src/cli/namestore/test_namestore_zonefile_import.sh b/src/cli/namestore/test_namestore_zonefile_import.sh
new file mode 100755
index 000000000..d6345257f
--- /dev/null
+++ b/src/cli/namestore/test_namestore_zonefile_import.sh
@@ -0,0 +1,33 @@
1#!/bin/sh
2# This file is in the public domain.
3trap "gnunet-arm -e -c test_namestore_api.conf" INT
4
5LOCATION=$(which gnunet-config)
6if [ -z $LOCATION ]
7then
8 LOCATION="gnunet-config"
9fi
10$LOCATION --version 1> /dev/null
11if test $? != 0
12then
13 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
14 exit 77
15fi
16
17rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME`
18which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5"
19
20MY_EGO="myego"
21gnunet-arm -s -c test_namestore_api.conf
22gnunet-identity -C $MY_EGO -c test_namestore_api.conf
23gnunet-namestore-zonefile -c test_namestore_api.conf < example_zonefile
24res=$?
25gnunet-identity -D $MY_EGO -c test_namestore_api.conf
26gnunet-arm -e -c test_namestore_api.conf
27
28if [ $res != 0 ]; then
29 echo "FAIL: Zone import failed."
30 exit 1
31fi
32
33