diff options
Diffstat (limited to 'src/cli/namestore')
-rw-r--r-- | src/cli/namestore/.gitignore | 6 | ||||
-rw-r--r-- | src/cli/namestore/Makefile.am | 70 | ||||
-rw-r--r-- | src/cli/namestore/example_zonefile | 27 | ||||
-rw-r--r-- | src/cli/namestore/gnunet-namestore-dbtool.c | 199 | ||||
-rw-r--r-- | src/cli/namestore/gnunet-namestore-zonefile.c | 729 | ||||
-rw-r--r-- | src/cli/namestore/gnunet-namestore.c | 2118 | ||||
-rw-r--r-- | src/cli/namestore/gnunet-zoneimport.c | 1882 | ||||
-rw-r--r-- | src/cli/namestore/meson.build | 38 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_box_lightest.sh | 63 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_delete.sh | 68 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_lookup.sh | 63 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_put.sh | 55 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_put_multiple.sh | 112 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_put_stdin.sh | 68 | ||||
-rwxr-xr-x | src/cli/namestore/test_namestore_zonefile_import.sh | 33 |
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 @@ | |||
1 | gnunet-namestore | ||
2 | gnunet-namestore-dbtool | ||
3 | gnunet-namestore-zonefile | ||
4 | gnunet-namestore-fcfsd | ||
5 | gnunet-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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | sqldir = $(prefix)/share/gnunet/sql/ | ||
11 | |||
12 | if USE_COVERAGE | ||
13 | AM_CFLAGS = --coverage -O0 | ||
14 | XLIBS = -lgcov | ||
15 | endif | ||
16 | |||
17 | |||
18 | bin_PROGRAMS = \ | ||
19 | gnunet-namestore \ | ||
20 | gnunet-namestore-dbtool \ | ||
21 | gnunet-namestore-zonefile \ | ||
22 | gnunet-zoneimport | ||
23 | |||
24 | gnunet_namestore_zonefile_SOURCES = \ | ||
25 | gnunet-namestore-zonefile.c | ||
26 | gnunet_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 | |||
33 | gnunet_zoneimport_SOURCES = \ | ||
34 | gnunet-zoneimport.c | ||
35 | gnunet_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 | |||
43 | gnunet_namestore_SOURCES = \ | ||
44 | gnunet-namestore.c | ||
45 | gnunet_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 | |||
52 | gnunet_namestore_dbtool_SOURCES = \ | ||
53 | gnunet-namestore-dbtool.c | ||
54 | gnunet_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 | |||
61 | check_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 | |||
68 | EXTRA_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 | ||
3 | example.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 ) | ||
9 | example.com. IN NS ns ; ns.example.com is a nameserver for example.com | ||
10 | example.com. IN NS ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com | ||
11 | example.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 | ||
14 | b.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 | ||
16 | ns 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 | ||
18 | www IN CNAME example.com. ; www.example.com is an alias for example.com | ||
19 | wwwtest IN CNAME www ; wwwtest.example.com is another alias for www.example.com | ||
20 | mail IN A 192.0.2.3 ; IPv4 address for mail.example.com | ||
21 | mail2 IN A 192.0.2.4 ; IPv4 address for mail2.example.com | ||
22 | mail3 IN A 192.0.2.5 ; IPv4 address for mail3.example.com | ||
23 | |||
24 | mail3 IN TXT "This is ; quoted" ; A quoted comment separator | ||
25 | $ORIGIN example.de. | ||
26 | www2 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 | */ | ||
33 | static char *pluginname; | ||
34 | |||
35 | /** | ||
36 | * Reset argument | ||
37 | */ | ||
38 | static int reset; | ||
39 | |||
40 | /** | ||
41 | * Initialize argument | ||
42 | */ | ||
43 | static int init; | ||
44 | |||
45 | /** | ||
46 | * Return code | ||
47 | */ | ||
48 | static int ret = 0; | ||
49 | |||
50 | /** | ||
51 | * Task run on shutdown. Cleans up everything. | ||
52 | * | ||
53 | * @param cls unused | ||
54 | */ | ||
55 | static void | ||
56 | do_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 | */ | ||
72 | static void | ||
73 | run (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 | */ | ||
153 | int | ||
154 | main (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 | */ | ||
46 | static struct GNUNET_GNSRECORD_Data rd[MAX_RECORDS_PER_NAME]; | ||
47 | |||
48 | /** | ||
49 | * Current record $TTL to use | ||
50 | */ | ||
51 | static struct GNUNET_TIME_Relative ttl; | ||
52 | |||
53 | /** | ||
54 | * Current origin | ||
55 | */ | ||
56 | static char origin[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; | ||
57 | |||
58 | /** | ||
59 | * Number of records for currently parsed set | ||
60 | */ | ||
61 | static unsigned int rd_count = 0; | ||
62 | |||
63 | /** | ||
64 | * Return code | ||
65 | */ | ||
66 | static int ret = 0; | ||
67 | |||
68 | /** | ||
69 | * Name of the ego | ||
70 | */ | ||
71 | static char *ego_name = NULL; | ||
72 | |||
73 | /** | ||
74 | * Currently read line or NULL on EOF | ||
75 | */ | ||
76 | static char *res; | ||
77 | |||
78 | /** | ||
79 | * Statistics, how many published record sets | ||
80 | */ | ||
81 | static unsigned int published_sets = 0; | ||
82 | |||
83 | /** | ||
84 | * Statistics, how many records published in aggregate | ||
85 | */ | ||
86 | static unsigned int published_records = 0; | ||
87 | |||
88 | |||
89 | /** | ||
90 | * Handle to identity lookup. | ||
91 | */ | ||
92 | static struct GNUNET_IDENTITY_EgoLookup *el; | ||
93 | |||
94 | /** | ||
95 | * Private key for the our zone. | ||
96 | */ | ||
97 | static struct GNUNET_CRYPTO_PrivateKey zone_pkey; | ||
98 | |||
99 | /** | ||
100 | * Queue entry for the 'add' operation. | ||
101 | */ | ||
102 | static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; | ||
103 | |||
104 | /** | ||
105 | * Handle to the namestore. | ||
106 | */ | ||
107 | static struct GNUNET_NAMESTORE_Handle *ns; | ||
108 | |||
109 | /** | ||
110 | * Origin create operations | ||
111 | */ | ||
112 | static struct GNUNET_IDENTITY_Operation *id_op; | ||
113 | |||
114 | /** | ||
115 | * Handle to IDENTITY | ||
116 | */ | ||
117 | static struct GNUNET_IDENTITY_Handle *id; | ||
118 | |||
119 | /** | ||
120 | * Current configurataion | ||
121 | */ | ||
122 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
123 | |||
124 | /** | ||
125 | * Scheduled parse task | ||
126 | */ | ||
127 | static struct GNUNET_SCHEDULER_Task *parse_task; | ||
128 | |||
129 | /** | ||
130 | * The current state of the parser | ||
131 | */ | ||
132 | static int state; | ||
133 | |||
134 | enum 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 | */ | ||
158 | static void | ||
159 | do_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 | |||
186 | static void | ||
187 | parse (void *cls); | ||
188 | |||
189 | static char* | ||
190 | trim (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 | |||
223 | static char* | ||
224 | next_token (char *token) | ||
225 | { | ||
226 | char *next = token; | ||
227 | while (*next == ' ') | ||
228 | next++; | ||
229 | return next; | ||
230 | } | ||
231 | |||
232 | static int | ||
233 | parse_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 | |||
254 | static int | ||
255 | parse_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 | |||
269 | static void | ||
270 | origin_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 | |||
286 | static void | ||
287 | origin_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 | |||
307 | static void | ||
308 | add_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 | */ | ||
352 | static void | ||
353 | parse (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 | |||
634 | static void | ||
635 | identity_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 | |||
659 | static void | ||
660 | run (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 | */ | ||
696 | int | ||
697 | main (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 | */ | ||
46 | struct 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 | */ | ||
62 | struct 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 | */ | ||
88 | struct 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 | */ | ||
114 | static struct GNUNET_NAMESTORE_Handle *ns; | ||
115 | |||
116 | /** | ||
117 | * Private key for the our zone. | ||
118 | */ | ||
119 | static struct GNUNET_CRYPTO_PrivateKey zone_pkey; | ||
120 | |||
121 | /** | ||
122 | * Identity service handle | ||
123 | */ | ||
124 | static struct GNUNET_IDENTITY_Handle *idh; | ||
125 | |||
126 | /** | ||
127 | * Name of the ego controlling the zone. | ||
128 | */ | ||
129 | static char *ego_name; | ||
130 | |||
131 | /** | ||
132 | * Queue entry for the 'add-uri' operation. | ||
133 | */ | ||
134 | static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri; | ||
135 | |||
136 | /** | ||
137 | * Queue entry for the 'add' operation. | ||
138 | */ | ||
139 | static struct GNUNET_NAMESTORE_QueueEntry *add_qe; | ||
140 | |||
141 | /** | ||
142 | * Queue entry for the 'lookup' operation. | ||
143 | */ | ||
144 | static struct GNUNET_NAMESTORE_QueueEntry *get_qe; | ||
145 | |||
146 | /** | ||
147 | * Queue entry for the 'reverse lookup' operation (in combination with a name). | ||
148 | */ | ||
149 | static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe; | ||
150 | |||
151 | /** | ||
152 | * Marked record list | ||
153 | */ | ||
154 | static struct MarkedRecord *marked_head; | ||
155 | |||
156 | /** | ||
157 | * Marked record list | ||
158 | */ | ||
159 | static struct MarkedRecord *marked_tail; | ||
160 | |||
161 | /** | ||
162 | * Configuration handle | ||
163 | */ | ||
164 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
165 | |||
166 | /** | ||
167 | * Ego list | ||
168 | */ | ||
169 | static struct EgoEntry *ego_head; | ||
170 | |||
171 | /** | ||
172 | * Ego list | ||
173 | */ | ||
174 | static struct EgoEntry *ego_tail; | ||
175 | |||
176 | /** | ||
177 | * List iterator for the 'list' operation. | ||
178 | */ | ||
179 | static struct GNUNET_NAMESTORE_ZoneIterator *list_it; | ||
180 | |||
181 | /** | ||
182 | * Run in read from stdin mode. | ||
183 | */ | ||
184 | static int read_from_stdin; | ||
185 | |||
186 | /** | ||
187 | * Desired action is to list records. | ||
188 | */ | ||
189 | static int list; | ||
190 | |||
191 | /** | ||
192 | * Desired action is to add a record. | ||
193 | */ | ||
194 | static int add; | ||
195 | |||
196 | /** | ||
197 | * Desired action is to remove a record. | ||
198 | */ | ||
199 | static int del; | ||
200 | |||
201 | /** | ||
202 | * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE) | ||
203 | */ | ||
204 | static int is_public; | ||
205 | |||
206 | /** | ||
207 | * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW) | ||
208 | */ | ||
209 | static int is_shadow; | ||
210 | |||
211 | /** | ||
212 | * Is record a maintenance record (#GNUNET_GNSRECORD_RF_MAINTENANCE) | ||
213 | */ | ||
214 | static int is_maintenance; | ||
215 | |||
216 | /** | ||
217 | * Filter private records | ||
218 | */ | ||
219 | static int omit_private; | ||
220 | |||
221 | /** | ||
222 | * Output in recordline format | ||
223 | */ | ||
224 | static int output_recordline; | ||
225 | |||
226 | |||
227 | /** | ||
228 | * Purge zone contents | ||
229 | */ | ||
230 | static int purge_zone; | ||
231 | |||
232 | /** | ||
233 | * Do not filter maintenance records | ||
234 | */ | ||
235 | static int include_maintenance; | ||
236 | |||
237 | /** | ||
238 | * Purge orphaned records | ||
239 | */ | ||
240 | static int purge_orphaned; | ||
241 | |||
242 | /** | ||
243 | * List records and zone keys of orphaned records | ||
244 | */ | ||
245 | static int list_orphaned; | ||
246 | |||
247 | /** | ||
248 | * Queue entry for the 'del' operation. | ||
249 | */ | ||
250 | static struct GNUNET_NAMESTORE_QueueEntry *del_qe; | ||
251 | |||
252 | /** | ||
253 | * Queue entry for the 'set/replace' operation. | ||
254 | */ | ||
255 | static struct GNUNET_NAMESTORE_QueueEntry *set_qe; | ||
256 | |||
257 | /** | ||
258 | * Queue entry for begin/commit | ||
259 | */ | ||
260 | static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; | ||
261 | |||
262 | /** | ||
263 | * Name of the records to add/list/remove. | ||
264 | */ | ||
265 | static char *name; | ||
266 | |||
267 | /** | ||
268 | * Value of the record to add/remove. | ||
269 | */ | ||
270 | static char *value; | ||
271 | |||
272 | /** | ||
273 | * URI to import. | ||
274 | */ | ||
275 | static char *uri; | ||
276 | |||
277 | /** | ||
278 | * Reverse lookup to perform. | ||
279 | */ | ||
280 | static char *reverse_pkey; | ||
281 | |||
282 | /** | ||
283 | * Type of the record to add/remove, NULL to remove all. | ||
284 | */ | ||
285 | static char *typestring; | ||
286 | |||
287 | /** | ||
288 | * Desired expiration time. | ||
289 | */ | ||
290 | static char *expirationstring; | ||
291 | |||
292 | /** | ||
293 | * Desired nick name. | ||
294 | */ | ||
295 | static char *nickstring; | ||
296 | |||
297 | /** | ||
298 | * Global return value | ||
299 | */ | ||
300 | static int ret; | ||
301 | |||
302 | /** | ||
303 | * Type string converted to DNS type value. | ||
304 | */ | ||
305 | static uint32_t type; | ||
306 | |||
307 | /** | ||
308 | * Value in binary format. | ||
309 | */ | ||
310 | static void *data; | ||
311 | |||
312 | /** | ||
313 | * Number of bytes in #data. | ||
314 | */ | ||
315 | static size_t data_size; | ||
316 | |||
317 | /** | ||
318 | * Expiration string converted to numeric value. | ||
319 | */ | ||
320 | static uint64_t etime; | ||
321 | |||
322 | /** | ||
323 | * Is expiration time relative or absolute time? | ||
324 | */ | ||
325 | static int etime_is_rel = GNUNET_SYSERR; | ||
326 | |||
327 | /** | ||
328 | * Monitor handle. | ||
329 | */ | ||
330 | static struct GNUNET_NAMESTORE_ZoneMonitor *zm; | ||
331 | |||
332 | /** | ||
333 | * Enables monitor mode. | ||
334 | */ | ||
335 | static int monitor; | ||
336 | |||
337 | /** | ||
338 | * Entry in record set for processing records in bulk. | ||
339 | */ | ||
340 | static struct RecordSetEntry *recordset; | ||
341 | |||
342 | /** | ||
343 | * Purge task | ||
344 | */ | ||
345 | static 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 | */ | ||
355 | static int | ||
356 | parse_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 | |||
400 | static int | ||
401 | parse_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 | |||
488 | static void | ||
489 | reset_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 | */ | ||
603 | static void | ||
604 | do_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 | |||
636 | static void | ||
637 | process_command_stdin (); | ||
638 | |||
639 | |||
640 | static void | ||
641 | finish_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 | |||
653 | static void | ||
654 | add_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 | |||
672 | static void | ||
673 | del_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 | |||
687 | static void | ||
688 | purge_next_record (void *cls); | ||
689 | |||
690 | static void | ||
691 | marked_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 | |||
704 | static void | ||
705 | purge_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 | */ | ||
734 | static void | ||
735 | zone_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 | */ | ||
752 | static void | ||
753 | zone_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 | |||
763 | static void | ||
764 | collect_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 | |||
781 | static void | ||
782 | collect_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 | */ | ||
820 | static void | ||
821 | display_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 | |||
934 | static void | ||
935 | purge_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 | |||
950 | static void | ||
951 | purge_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 | */ | ||
975 | static void | ||
976 | display_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 | */ | ||
1000 | static void | ||
1001 | display_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 | */ | ||
1025 | static void | ||
1026 | display_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 | */ | ||
1045 | static void | ||
1046 | sync_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 | */ | ||
1058 | static void | ||
1059 | monitor_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 | */ | ||
1071 | static void | ||
1072 | lookup_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 | */ | ||
1084 | static void | ||
1085 | add_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 | */ | ||
1105 | static void | ||
1106 | get_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 | */ | ||
1180 | static void | ||
1181 | reverse_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 | */ | ||
1199 | static void | ||
1200 | handle_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 | */ | ||
1222 | static void | ||
1223 | del_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 | */ | ||
1244 | static void | ||
1245 | del_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 | |||
1322 | static void | ||
1323 | replace_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 | */ | ||
1345 | static void | ||
1346 | run_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 | |||
1748 | static int | ||
1749 | get_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 | |||
1787 | static void | ||
1788 | process_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 | */ | ||
1887 | static void | ||
1888 | id_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 | */ | ||
1931 | static void | ||
1932 | run (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 | */ | ||
1972 | int | ||
1973 | main (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 | */ | ||
62 | static 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 | */ | ||
76 | struct 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 | */ | ||
103 | struct 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 | */ | ||
127 | struct 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 | */ | ||
212 | static unsigned int map_size = 1024; | ||
213 | |||
214 | /** | ||
215 | * Handle to the identity service. | ||
216 | */ | ||
217 | static struct GNUNET_IDENTITY_Handle *id; | ||
218 | |||
219 | /** | ||
220 | * Namestore handle. | ||
221 | */ | ||
222 | static struct GNUNET_NAMESTORE_Handle *ns; | ||
223 | |||
224 | /** | ||
225 | * Handle to the statistics service. | ||
226 | */ | ||
227 | static struct GNUNET_STATISTICS_Handle *stats; | ||
228 | |||
229 | /** | ||
230 | * Context for DNS resolution. | ||
231 | */ | ||
232 | static struct GNUNET_DNSSTUB_Context *ctx; | ||
233 | |||
234 | /** | ||
235 | * The number of DNS queries that are outstanding | ||
236 | */ | ||
237 | static unsigned int pending; | ||
238 | |||
239 | /** | ||
240 | * The number of NAMESTORE record store operations that are outstanding | ||
241 | */ | ||
242 | static unsigned int pending_rs; | ||
243 | |||
244 | /** | ||
245 | * Number of lookups we performed overall. | ||
246 | */ | ||
247 | static unsigned int lookups; | ||
248 | |||
249 | /** | ||
250 | * Number of records we had cached. | ||
251 | */ | ||
252 | static unsigned int cached; | ||
253 | |||
254 | /** | ||
255 | * How many hostnames did we reject (malformed). | ||
256 | */ | ||
257 | static unsigned int rejects; | ||
258 | |||
259 | /** | ||
260 | * Number of lookups that failed. | ||
261 | */ | ||
262 | static unsigned int failures; | ||
263 | |||
264 | /** | ||
265 | * Number of records we found. | ||
266 | */ | ||
267 | static unsigned int records; | ||
268 | |||
269 | /** | ||
270 | * Number of record sets given to namestore. | ||
271 | */ | ||
272 | static 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 | */ | ||
278 | static struct GNUNET_CONTAINER_Heap *req_heap; | ||
279 | |||
280 | /** | ||
281 | * Active requests are kept in a DLL. | ||
282 | */ | ||
283 | static struct Request *req_head; | ||
284 | |||
285 | /** | ||
286 | * Active requests are kept in a DLL. | ||
287 | */ | ||
288 | static struct Request *req_tail; | ||
289 | |||
290 | /** | ||
291 | * Main task. | ||
292 | */ | ||
293 | static 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 | */ | ||
300 | static struct GNUNET_CONTAINER_MultiHashMap *ns_pending; | ||
301 | |||
302 | /** | ||
303 | * Current zone iteration handle. | ||
304 | */ | ||
305 | static struct GNUNET_NAMESTORE_ZoneIterator *zone_it; | ||
306 | |||
307 | /** | ||
308 | * Head of list of zones we are managing. | ||
309 | */ | ||
310 | static struct Zone *zone_head; | ||
311 | |||
312 | /** | ||
313 | * Tail of list of zones we are managing. | ||
314 | */ | ||
315 | static struct Zone *zone_tail; | ||
316 | |||
317 | /** | ||
318 | * After how many more results must #ns_lookup_result_cb() ask | ||
319 | * the namestore for more? | ||
320 | */ | ||
321 | static uint64_t ns_iterator_trigger_next; | ||
322 | |||
323 | /** | ||
324 | * Number of DNS requests counted in latency total. | ||
325 | */ | ||
326 | static uint64_t total_dns_latency_cnt; | ||
327 | |||
328 | /** | ||
329 | * Sum of DNS latencies observed. | ||
330 | */ | ||
331 | static struct GNUNET_TIME_Relative total_dns_latency; | ||
332 | |||
333 | /** | ||
334 | * Number of records processed (DNS lookup, no NAMESTORE) in total. | ||
335 | */ | ||
336 | static uint64_t total_reg_proc_dns; | ||
337 | |||
338 | /** | ||
339 | * Number of records processed (DNS lookup, with NAMESTORE) in total. | ||
340 | */ | ||
341 | static uint64_t total_reg_proc_dns_ns; | ||
342 | |||
343 | /** | ||
344 | * Start time of the regular processing. | ||
345 | */ | ||
346 | static struct GNUNET_TIME_Absolute start_time_reg_proc; | ||
347 | |||
348 | /** | ||
349 | * Last time we worked before going idle. | ||
350 | */ | ||
351 | static struct GNUNET_TIME_Absolute sleep_time_reg_proc; | ||
352 | |||
353 | /** | ||
354 | * Time we slept just waiting for work. | ||
355 | */ | ||
356 | static 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 | */ | ||
365 | typedef 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 | */ | ||
377 | static void | ||
378 | for_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 | */ | ||
410 | static const char * | ||
411 | get_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 | */ | ||
441 | static void * | ||
442 | build_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 | */ | ||
490 | static void | ||
491 | free_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 | */ | ||
509 | static void | ||
510 | free_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 | */ | ||
522 | static void | ||
523 | process_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 | */ | ||
531 | static void | ||
532 | insert_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 | */ | ||
555 | static void | ||
556 | add_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 | */ | ||
578 | struct 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 | */ | ||
603 | static void | ||
604 | check_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 | */ | ||
712 | struct 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 | */ | ||
733 | static void | ||
734 | process_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 | |||
935 | static void | ||
936 | store_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 | */ | ||
1029 | static void | ||
1030 | process_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 | */ | ||
1180 | static void | ||
1181 | process_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 | */ | ||
1277 | static int | ||
1278 | free_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 | */ | ||
1294 | static void | ||
1295 | do_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 | */ | ||
1371 | static void | ||
1372 | iterate_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 | */ | ||
1381 | static void | ||
1382 | ns_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 | */ | ||
1404 | static void | ||
1405 | ns_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 | */ | ||
1504 | static void | ||
1505 | queue (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 | */ | ||
1576 | static int | ||
1577 | move_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 | */ | ||
1594 | static void | ||
1595 | iterate_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 | */ | ||
1662 | static void | ||
1663 | process_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 | */ | ||
1737 | static void | ||
1738 | identity_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 | */ | ||
1780 | static void | ||
1781 | run (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 | */ | ||
1837 | int | ||
1838 | main (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 @@ | |||
1 | executable ('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')) | ||
10 | executable ('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')) | ||
19 | executable ('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')) | ||
28 | executable ('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 | ||
2 | CONFIGURATION="test_namestore_api.conf" | ||
3 | trap "gnunet-arm -e -c $CONFIGURATION" SIGINT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` | ||
18 | TEST_RECORD_NAME_DNS="trust" | ||
19 | TEST_RECORD_VALUE_SMIMEA="49152 49153 53 0 0 1 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" | ||
20 | TEST_RECORD_VALUE_URI="49152 49152 256 10 10 \"http://lightest.nletlabs.nl/\"" | ||
21 | which timeout &> /dev/null && DO_TIMEOUT="timeout 5" | ||
22 | |||
23 | function start_peer | ||
24 | { | ||
25 | gnunet-arm -s -c $CONFIGURATION | ||
26 | gnunet-identity -C testego -c $CONFIGURATION | ||
27 | } | ||
28 | |||
29 | function stop_peer | ||
30 | { | ||
31 | gnunet-identity -D testego -c $CONFIGURATION | ||
32 | gnunet-arm -e -c $CONFIGURATION | ||
33 | } | ||
34 | |||
35 | |||
36 | start_peer | ||
37 | # Create a public SMIMEA record | ||
38 | gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t BOX -V "$TEST_RECORD_VALUE_SMIMEA" -e never -c $CONFIGURATION | ||
39 | NAMESTORE_RES=$? | ||
40 | |||
41 | if [ $NAMESTORE_RES = 0 ] | ||
42 | then | ||
43 | echo "PASS: Creating boxed name in namestore SMIMEA" | ||
44 | else | ||
45 | echo "FAIL: Creating boxed name in namestore failed with $NAMESTORE_RES." | ||
46 | stop_peer | ||
47 | exit 1 | ||
48 | fi | ||
49 | |||
50 | # Create a public URI record | ||
51 | gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t BOX -V "$TEST_RECORD_VALUE_URI" -e never -c $CONFIGURATION | ||
52 | NAMESTORE_RES=$? | ||
53 | |||
54 | if [ $NAMESTORE_RES = 0 ] | ||
55 | then | ||
56 | echo "PASS: Creating boxed name in namestore URI" | ||
57 | else | ||
58 | echo "FAIL: Creating boxed name in namestore failed with $NAMESTORE_RES." | ||
59 | stop_peer | ||
60 | exit 1 | ||
61 | fi | ||
62 | |||
63 | stop_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 | ||
2 | CONFIGURATION="test_namestore_api.conf" | ||
3 | trap "gnunet-arm -e -c $CONFIGURATION" SIGINT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` | ||
18 | TEST_DOMAIN_PLUS="www.gnu" | ||
19 | TEST_DOMAIN_DNS="www3.gnu" | ||
20 | TEST_IP_PLUS="127.0.0.1" | ||
21 | TEST_IP_DNS="131.159.74.67" | ||
22 | TEST_RECORD_CNAME_SERVER="server" | ||
23 | TEST_RECORD_CNAME_PLUS="server.+" | ||
24 | TEST_RECORD_CNAME_DNS="gnunet.org" | ||
25 | TEST_RECORD_NAME_SERVER="server" | ||
26 | TEST_RECORD_NAME_PLUS="www" | ||
27 | TEST_RECORD_NAME_DNS="www3" | ||
28 | which timeout &> /dev/null && DO_TIMEOUT="timeout 5" | ||
29 | |||
30 | function start_peer | ||
31 | { | ||
32 | gnunet-arm -s -c $CONFIGURATION | ||
33 | gnunet-identity -C testego -c $CONFIGURATION | ||
34 | } | ||
35 | |||
36 | function stop_peer | ||
37 | { | ||
38 | gnunet-identity -D testego -c $CONFIGURATION | ||
39 | gnunet-arm -e -c $CONFIGURATION | ||
40 | } | ||
41 | |||
42 | |||
43 | start_peer | ||
44 | # Create a public record | ||
45 | gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION | ||
46 | # Delete record | ||
47 | gnunet-namestore -p -z testego -d -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION | ||
48 | # List all records | ||
49 | OUTPUT=`gnunet-namestore -p -z testego -D` | ||
50 | FOUND_IP=false | ||
51 | FOUND_NAME=false | ||
52 | for 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 | ||
61 | stop_peer | ||
62 | |||
63 | |||
64 | if [ $FOUND_IP = true ] | ||
65 | then | ||
66 | echo "FAIL: Delete name in namestore: IP returned" | ||
67 | exit 1 | ||
68 | fi | ||
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 | ||
2 | CONFIGURATION="test_namestore_api.conf" | ||
3 | trap "gnunet-arm -e -c $CONFIGURATION" SIGINT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` | ||
18 | TEST_IP_PLUS="127.0.0.1" | ||
19 | TEST_RECORD_NAME_DNS="www3" | ||
20 | which timeout &> /dev/null && DO_TIMEOUT="timeout 5" | ||
21 | |||
22 | # start peer | ||
23 | gnunet-arm -s -c $CONFIGURATION | ||
24 | gnunet-identity -C testego -c $CONFIGURATION | ||
25 | |||
26 | # Create a public record | ||
27 | gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION | ||
28 | NAMESTORE_RES=$? | ||
29 | # Lookup specific name | ||
30 | OUTPUT=`gnunet-namestore -p -z testego -n $TEST_RECORD_NAME_DNS -D` | ||
31 | |||
32 | |||
33 | FOUND_IP=false | ||
34 | FOUND_NAME=false | ||
35 | for 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 | ||
45 | done | ||
46 | # stop peer | ||
47 | gnunet-identity -D testego -c $CONFIGURATION | ||
48 | gnunet-arm -e -c $CONFIGURATION | ||
49 | |||
50 | |||
51 | if [ $FOUND_NAME = true -a $FOUND_IP = true ] | ||
52 | then | ||
53 | echo "PASS: Lookup name in namestore" | ||
54 | exit 0 | ||
55 | elif [ $FOUND_NAME = false ] | ||
56 | then | ||
57 | echo "FAIL: Lookup name in namestore: name not returned" | ||
58 | exit 1 | ||
59 | elif [ $FOUND_IP = false ] | ||
60 | then | ||
61 | echo "FAIL: Lookup name in namestore: IP not returned" | ||
62 | exit 1 | ||
63 | fi | ||
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 | ||
2 | CONFIGURATION="test_namestore_api.conf" | ||
3 | trap "gnunet-arm -e -c $CONFIGURATION" SIGINT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` | ||
18 | TEST_DOMAIN_PLUS="www.gnu" | ||
19 | TEST_DOMAIN_DNS="www3.gnu" | ||
20 | TEST_IP_PLUS="127.0.0.1" | ||
21 | TEST_IP_DNS="131.159.74.67" | ||
22 | TEST_RECORD_CNAME_SERVER="server" | ||
23 | TEST_RECORD_CNAME_PLUS="server.+" | ||
24 | TEST_RECORD_CNAME_DNS="gnunet.org" | ||
25 | TEST_RECORD_NAME_SERVER="server" | ||
26 | TEST_RECORD_NAME_PLUS="www" | ||
27 | TEST_RECORD_NAME_DNS="www3" | ||
28 | which timeout &> /dev/null && DO_TIMEOUT="timeout 5" | ||
29 | |||
30 | function start_peer | ||
31 | { | ||
32 | gnunet-arm -s -c $CONFIGURATION | ||
33 | gnunet-identity -C testego -c $CONFIGURATION | ||
34 | } | ||
35 | |||
36 | function stop_peer | ||
37 | { | ||
38 | gnunet-identity -D testego -c $CONFIGURATION | ||
39 | gnunet-arm -e -c $CONFIGURATION | ||
40 | } | ||
41 | |||
42 | |||
43 | start_peer | ||
44 | # Create a public record | ||
45 | gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION | ||
46 | NAMESTORE_RES=$? | ||
47 | stop_peer | ||
48 | |||
49 | if [ $NAMESTORE_RES = 0 ] | ||
50 | then | ||
51 | echo "PASS: Creating name in namestore" | ||
52 | else | ||
53 | echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES." | ||
54 | exit 1 | ||
55 | fi | ||
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 | ||
4 | if ! [ -x "$(command -v gnunet-namestore)" ]; then | ||
5 | echo 'bind/named is not installed' >&2 | ||
6 | exit 1 | ||
7 | fi | ||
8 | |||
9 | # Check if gnunet is running | ||
10 | gnunet-arm -I 2&>1 /dev/null | ||
11 | ret=$? | ||
12 | if [ 0 -ne $ret ]; then | ||
13 | echo 'gnunet services are not running' | ||
14 | exit 1 | ||
15 | fi | ||
16 | |||
17 | ## GNUNET part | ||
18 | # Check if identity exists and deletes and readds it to get rid of entries in zone | ||
19 | gnunet-identity -d | grep randomtestingid 2>&1 /dev/null | ||
20 | ret=$? | ||
21 | |||
22 | if [ 0 -ne $ret ]; then | ||
23 | gnunet-identity -D randomtestingid | ||
24 | gnunet-identity -C randomtestingid | ||
25 | fi | ||
26 | |||
27 | function get_record_type { | ||
28 | arr=$1 | ||
29 | typ=$(echo -n "${arr[0]}" | cut -d' ' -f1) | ||
30 | echo "$typ" | ||
31 | } | ||
32 | |||
33 | function get_value { | ||
34 | arr=$1 | ||
35 | val=$(echo -n "${arr[0]}" | cut -d' ' -f4-) | ||
36 | echo "$val" | ||
37 | } | ||
38 | |||
39 | function 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 | ||
52 | EOF | ||
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 | ||
65 | echo "Testing adding of single A record with -R" | ||
66 | declare -a arr=('A 1200 [r] 127.0.0.1') | ||
67 | testing test1 "${arr[@]}" | ||
68 | # 2 | ||
69 | echo "Testing adding of multiple A records with -R" | ||
70 | declare -a arr=('A 1200 [r] 127.0.0.1' 'A 2400 [r] 127.0.0.2') | ||
71 | testing test2 "${arr[@]}" | ||
72 | # 3 | ||
73 | echo "Testing adding of multiple different records with -R" | ||
74 | declare -a arr=('A 1200 [r] 127.0.0.1' 'AAAA 2400 [r] 2002::') | ||
75 | testing test3 "${arr[@]}" | ||
76 | # 4 | ||
77 | echo "Testing adding of single GNS2DNS record with -R" | ||
78 | declare -a arr=('GNS2DNS 86400 [r] gnu.org@127.0.0.1') | ||
79 | testing test4 "${arr[@]}" | ||
80 | # 5 | ||
81 | echo "Testing adding of single GNS2DNS shadow record with -R" | ||
82 | declare -a arr=('GNS2DNS 86409 [rs] gnu.org@127.0.0.250') | ||
83 | testing test5 "${arr[@]}" | ||
84 | # 6 | ||
85 | echo "Testing adding of multiple GNS2DNS record with -R" | ||
86 | declare -a arr=('GNS2DNS 1 [r] gnunet.org@127.0.0.1' 'GNS2DNS 3600 [s] gnunet.org@127.0.0.2') | ||
87 | testing test6 "${arr[@]}" | ||
88 | val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) | ||
89 | if [[ $val == *"127.0.0.1"* ]]; then | ||
90 | echo "shadow!" | ||
91 | fi | ||
92 | echo "Sleeping to let record expire" | ||
93 | sleep 5 | ||
94 | val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) | ||
95 | if [[ $val == *"127.0.0.2"* ]]; then | ||
96 | echo "no shadow!" | ||
97 | fi | ||
98 | # 7 | ||
99 | echo "Testing adding MX record with -R" | ||
100 | declare -a arr=('MX 3600 [r] 10,mail') | ||
101 | testing test7 "${arr[@]}" | ||
102 | # 8 | ||
103 | echo "Testing adding TXT record with -R" | ||
104 | declare -a arr=('TXT 3600 [r] Pretty_Unicorns') | ||
105 | testing 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 | ||
112 | gnunet-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 | ||
2 | CONFIGURATION="test_namestore_api.conf" | ||
3 | trap "gnunet-arm -e -c $CONFIGURATION" SIGINT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` | ||
18 | TEST_RECORD_NAME="www3" | ||
19 | TEST_RECORD_NAME2="www" | ||
20 | TEST_IP="8.7.6.5" | ||
21 | TEST_IP2="1.2.3.4" | ||
22 | |||
23 | which timeout &> /dev/null && DO_TIMEOUT="timeout 5" | ||
24 | |||
25 | function 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 | |||
32 | function 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 | |||
40 | start_peer | ||
41 | # Create a public record | ||
42 | EGOKEY=`gnunet-identity -d | grep testego2 | cut -d' ' -f3` | ||
43 | gnunet-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 | |||
54 | www7.$EGOKEY: | ||
55 | A 3600000000 [pr] $TEST_IP | ||
56 | |||
57 | EOF | ||
58 | NAMESTORE_RES=$? | ||
59 | gnunet-namestore -D -r -c $CONFIGURATION | ||
60 | stop_peer | ||
61 | |||
62 | if [ $NAMESTORE_RES = 0 ] | ||
63 | then | ||
64 | echo "PASS: Creating name in namestore" | ||
65 | else | ||
66 | echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES." | ||
67 | exit 1 | ||
68 | fi | ||
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. | ||
3 | trap "gnunet-arm -e -c test_namestore_api.conf" INT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | $LOCATION --version 1> /dev/null | ||
11 | if test $? != 0 | ||
12 | then | ||
13 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
19 | |||
20 | MY_EGO="myego" | ||
21 | gnunet-arm -s -c test_namestore_api.conf | ||
22 | gnunet-identity -C $MY_EGO -c test_namestore_api.conf | ||
23 | gnunet-namestore-zonefile -c test_namestore_api.conf < example_zonefile | ||
24 | res=$? | ||
25 | gnunet-identity -D $MY_EGO -c test_namestore_api.conf | ||
26 | gnunet-arm -e -c test_namestore_api.conf | ||
27 | |||
28 | if [ $res != 0 ]; then | ||
29 | echo "FAIL: Zone import failed." | ||
30 | exit 1 | ||
31 | fi | ||
32 | |||
33 | |||