diff options
Diffstat (limited to 'src/cli')
164 files changed, 28550 insertions, 0 deletions
diff --git a/src/cli/.gitignore b/src/cli/.gitignore new file mode 100644 index 000000000..cedad92f1 --- /dev/null +++ b/src/cli/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-revocation | ||
2 | gnunet-revocation-tvg | ||
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am new file mode 100644 index 000000000..6ad6dd70a --- /dev/null +++ b/src/cli/Makefile.am | |||
@@ -0,0 +1,21 @@ | |||
1 | SUBDIRS = \ | ||
2 | util \ | ||
3 | arm \ | ||
4 | statistics \ | ||
5 | peerstore \ | ||
6 | core \ | ||
7 | nat \ | ||
8 | nat-auto \ | ||
9 | nse \ | ||
10 | datastore \ | ||
11 | dht \ | ||
12 | identity \ | ||
13 | namecache \ | ||
14 | namestore \ | ||
15 | revocation \ | ||
16 | vpn \ | ||
17 | gns \ | ||
18 | fs \ | ||
19 | cadet \ | ||
20 | reclaim \ | ||
21 | messenger | ||
diff --git a/src/cli/arm/.gitignore b/src/cli/arm/.gitignore new file mode 100644 index 000000000..f010c20a1 --- /dev/null +++ b/src/cli/arm/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-arm | |||
diff --git a/src/cli/arm/Makefile.am b/src/cli/arm/Makefile.am new file mode 100644 index 000000000..2d41ab141 --- /dev/null +++ b/src/cli/arm/Makefile.am | |||
@@ -0,0 +1,45 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | libexecdir= $(pkglibdir)/libexec/ | ||
7 | |||
8 | if USE_COVERAGE | ||
9 | AM_CFLAGS = --coverage -O0 | ||
10 | XLIB = -lgcov | ||
11 | endif | ||
12 | |||
13 | bin_PROGRAMS = \ | ||
14 | gnunet-arm | ||
15 | |||
16 | gnunet_arm_SOURCES = \ | ||
17 | gnunet-arm.c | ||
18 | gnunet_arm_LDADD = \ | ||
19 | $(top_builddir)/src/service/arm/libgnunetarm.la \ | ||
20 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
21 | $(GN_LIBINTL) | ||
22 | |||
23 | # FIXME the respective conf file now resides in service/arm/ | ||
24 | #if HAVE_PYTHON | ||
25 | #check_SCRIPTS = \ | ||
26 | # test_gnunet_arm.py | ||
27 | #endif | ||
28 | |||
29 | if ENABLE_TEST_RUN | ||
30 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
31 | TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
32 | endif | ||
33 | |||
34 | #SUFFIXES = .py.in .py | ||
35 | #.py.in.py: | ||
36 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/$< > $@ | ||
37 | # chmod +x $@ | ||
38 | # | ||
39 | #test_gnunet_arm.py: test_gnunet_arm.py.in Makefile | ||
40 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/test_gnunet_arm.py.in > test_gnunet_arm.py | ||
41 | # chmod +x test_gnunet_arm.py | ||
42 | # | ||
43 | #EXTRA_DIST = \ | ||
44 | # test_arm_api_data.conf \ | ||
45 | # test_gnunet_arm.py.in | ||
diff --git a/src/cli/arm/gnunet-arm.c b/src/cli/arm/gnunet-arm.c new file mode 100644 index 000000000..ea3a012ab --- /dev/null +++ b/src/cli/arm/gnunet-arm.c | |||
@@ -0,0 +1,1065 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2012, 2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file arm/gnunet-arm.c | ||
23 | * @brief arm for writing a tool | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_arm_service.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | /** | ||
32 | * Set if we are to shutdown all services (including ARM). | ||
33 | */ | ||
34 | static int end; | ||
35 | |||
36 | /** | ||
37 | * Set if we are to start default services (including ARM). | ||
38 | */ | ||
39 | static int start; | ||
40 | |||
41 | /** | ||
42 | * Set if we are to stop/start default services (including ARM). | ||
43 | */ | ||
44 | static int restart; | ||
45 | |||
46 | /** | ||
47 | * Set if we should delete configuration and temp directory on exit. | ||
48 | */ | ||
49 | static int delete; | ||
50 | |||
51 | /** | ||
52 | * Set if we should not print status messages. | ||
53 | */ | ||
54 | static int quiet; | ||
55 | |||
56 | /** | ||
57 | * Set if we should print all services, including stopped ones. | ||
58 | */ | ||
59 | static int show_all; | ||
60 | |||
61 | /** | ||
62 | * Monitor ARM activity. | ||
63 | */ | ||
64 | static int monitor; | ||
65 | |||
66 | /** | ||
67 | * Set if we should print a list of currently running services. | ||
68 | */ | ||
69 | static int list; | ||
70 | |||
71 | /** | ||
72 | * Set to the name of a service to start. | ||
73 | */ | ||
74 | static char *init; | ||
75 | |||
76 | /** | ||
77 | * Set to the name of a service to kill. | ||
78 | */ | ||
79 | static char *term; | ||
80 | |||
81 | /** | ||
82 | * Set to the name of the config file used. | ||
83 | */ | ||
84 | static char *config_file; | ||
85 | |||
86 | /** | ||
87 | * Set to the directory where runtime files are stored. | ||
88 | */ | ||
89 | static char *dir; | ||
90 | |||
91 | /** | ||
92 | * Final status code. | ||
93 | */ | ||
94 | static int ret; | ||
95 | |||
96 | /** | ||
97 | * Connection with ARM. | ||
98 | */ | ||
99 | static struct GNUNET_ARM_Handle *h; | ||
100 | |||
101 | /** | ||
102 | * Monitor connection with ARM. | ||
103 | */ | ||
104 | static struct GNUNET_ARM_MonitorHandle *m; | ||
105 | |||
106 | /** | ||
107 | * Our configuration. | ||
108 | */ | ||
109 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
110 | |||
111 | /** | ||
112 | * Processing stage that we are in. Simple counter. | ||
113 | */ | ||
114 | static unsigned int phase; | ||
115 | |||
116 | /** | ||
117 | * User defined timestamp for completing operations. | ||
118 | */ | ||
119 | static struct GNUNET_TIME_Relative timeout; | ||
120 | |||
121 | /** | ||
122 | * Task to be run on timeout. | ||
123 | */ | ||
124 | static struct GNUNET_SCHEDULER_Task *timeout_task; | ||
125 | |||
126 | /** | ||
127 | * Do we want to give our stdout to gnunet-service-arm? | ||
128 | */ | ||
129 | static int no_stdout; | ||
130 | |||
131 | /** | ||
132 | * Do we want to give our stderr to gnunet-service-arm? | ||
133 | */ | ||
134 | static int no_stderr; | ||
135 | |||
136 | /** | ||
137 | * Handle for the task running the #action_loop(). | ||
138 | */ | ||
139 | static struct GNUNET_SCHEDULER_Task *al_task; | ||
140 | |||
141 | /** | ||
142 | * Current operation. | ||
143 | */ | ||
144 | static struct GNUNET_ARM_Operation *op; | ||
145 | |||
146 | /** | ||
147 | * Attempts to delete configuration file and GNUNET_HOME | ||
148 | * on ARM shutdown provided the end and delete options | ||
149 | * were specified when gnunet-arm was run. | ||
150 | */ | ||
151 | static void | ||
152 | delete_files () | ||
153 | { | ||
154 | GNUNET_log ( | ||
155 | GNUNET_ERROR_TYPE_DEBUG, | ||
156 | "Will attempt to remove configuration file %s and service directory %s\n", | ||
157 | config_file, | ||
158 | dir); | ||
159 | if (0 != unlink (config_file)) | ||
160 | { | ||
161 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
162 | _ ("Failed to remove configuration file %s\n"), | ||
163 | config_file); | ||
164 | } | ||
165 | if (GNUNET_OK != GNUNET_DISK_directory_remove (dir)) | ||
166 | { | ||
167 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
168 | _ ("Failed to remove servicehome directory %s\n"), | ||
169 | dir); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | |||
174 | /** | ||
175 | * Main continuation-passing-style loop. Runs the various | ||
176 | * jobs that we've been asked to do in order. | ||
177 | * | ||
178 | * @param cls closure, unused | ||
179 | */ | ||
180 | static void | ||
181 | shutdown_task (void *cls) | ||
182 | { | ||
183 | (void) cls; | ||
184 | if (NULL != al_task) | ||
185 | { | ||
186 | GNUNET_SCHEDULER_cancel (al_task); | ||
187 | al_task = NULL; | ||
188 | } | ||
189 | if (NULL != op) | ||
190 | { | ||
191 | GNUNET_ARM_operation_cancel (op); | ||
192 | op = NULL; | ||
193 | } | ||
194 | if (NULL != h) | ||
195 | { | ||
196 | GNUNET_ARM_disconnect (h); | ||
197 | h = NULL; | ||
198 | } | ||
199 | if (NULL != m) | ||
200 | { | ||
201 | GNUNET_ARM_monitor_stop (m); | ||
202 | m = NULL; | ||
203 | } | ||
204 | if (NULL != timeout_task) | ||
205 | { | ||
206 | GNUNET_SCHEDULER_cancel (timeout_task); | ||
207 | timeout_task = NULL; | ||
208 | } | ||
209 | if ( (GNUNET_YES == end) && | ||
210 | (GNUNET_YES == delete) ) | ||
211 | delete_files (); | ||
212 | GNUNET_CONFIGURATION_destroy (cfg); | ||
213 | cfg = NULL; | ||
214 | } | ||
215 | |||
216 | |||
217 | /** | ||
218 | * Returns a string interpretation of @a rs | ||
219 | * | ||
220 | * @param rs the request status from ARM | ||
221 | * @return a string interpretation of the request status | ||
222 | */ | ||
223 | static const char * | ||
224 | req_string (enum GNUNET_ARM_RequestStatus rs) | ||
225 | { | ||
226 | switch (rs) | ||
227 | { | ||
228 | case GNUNET_ARM_REQUEST_SENT_OK: | ||
229 | return _ ("Message was sent successfully"); | ||
230 | |||
231 | case GNUNET_ARM_REQUEST_DISCONNECTED: | ||
232 | return _ ("We disconnected from ARM before we could send a request"); | ||
233 | } | ||
234 | return _ ("Unknown request status"); | ||
235 | } | ||
236 | |||
237 | |||
238 | /** | ||
239 | * Returns a string interpretation of the @a result | ||
240 | * | ||
241 | * @param result the arm result | ||
242 | * @return a string interpretation | ||
243 | */ | ||
244 | static const char * | ||
245 | ret_string (enum GNUNET_ARM_Result result) | ||
246 | { | ||
247 | switch (result) | ||
248 | { | ||
249 | case GNUNET_ARM_RESULT_STOPPED: | ||
250 | return _ ("is stopped"); | ||
251 | |||
252 | case GNUNET_ARM_RESULT_STARTING: | ||
253 | return _ ("is starting"); | ||
254 | |||
255 | case GNUNET_ARM_RESULT_STOPPING: | ||
256 | return _ ("is stopping"); | ||
257 | |||
258 | case GNUNET_ARM_RESULT_IS_STARTING_ALREADY: | ||
259 | return _ ("is starting already"); | ||
260 | |||
261 | case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY: | ||
262 | return _ ("is stopping already"); | ||
263 | |||
264 | case GNUNET_ARM_RESULT_IS_STARTED_ALREADY: | ||
265 | return _ ("is started already"); | ||
266 | |||
267 | case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY: | ||
268 | return _ ("is stopped already"); | ||
269 | |||
270 | case GNUNET_ARM_RESULT_IS_NOT_KNOWN: | ||
271 | return _ ("service is not known to ARM"); | ||
272 | |||
273 | case GNUNET_ARM_RESULT_START_FAILED: | ||
274 | return _ ("service failed to start"); | ||
275 | |||
276 | case GNUNET_ARM_RESULT_IN_SHUTDOWN: | ||
277 | return _ ("service cannot be manipulated because ARM is shutting down"); | ||
278 | } | ||
279 | return _ ("Unknown result code."); | ||
280 | } | ||
281 | |||
282 | |||
283 | /** | ||
284 | * Main task that runs our various operations in order. | ||
285 | * | ||
286 | * @param cls closure | ||
287 | */ | ||
288 | static void | ||
289 | action_loop (void *cls); | ||
290 | |||
291 | |||
292 | /** | ||
293 | * Function called whenever we connect to or disconnect from ARM. | ||
294 | * Termiantes the process if we fail to connect to the service on | ||
295 | * our first attempt. | ||
296 | * | ||
297 | * @param cls closure | ||
298 | * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected, | ||
299 | * #GNUNET_SYSERR on error. | ||
300 | */ | ||
301 | static void | ||
302 | conn_status (void *cls, | ||
303 | int connected) | ||
304 | { | ||
305 | static int once; | ||
306 | |||
307 | (void) cls; | ||
308 | if ( (GNUNET_SYSERR == connected) && | ||
309 | (0 == once) ) | ||
310 | { | ||
311 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
312 | _ ("Fatal error initializing ARM API.\n")); | ||
313 | GNUNET_SCHEDULER_shutdown (); | ||
314 | return; | ||
315 | } | ||
316 | once = 1; | ||
317 | } | ||
318 | |||
319 | |||
320 | /** | ||
321 | * We have requested ARM to be started, this function | ||
322 | * is called with the result of the operation. Informs the | ||
323 | * use of the result; on success, we continue with the event | ||
324 | * loop, on failure we terminate the process. | ||
325 | * | ||
326 | * @param cls closure unused | ||
327 | * @param rs what happened to our request | ||
328 | * @param result if the request was processed, this is the result | ||
329 | * according to ARM | ||
330 | */ | ||
331 | static void | ||
332 | start_callback (void *cls, | ||
333 | enum GNUNET_ARM_RequestStatus rs, | ||
334 | enum GNUNET_ARM_Result result) | ||
335 | { | ||
336 | (void) cls; | ||
337 | op = NULL; | ||
338 | if (GNUNET_ARM_REQUEST_SENT_OK != rs) | ||
339 | { | ||
340 | fprintf (stdout, | ||
341 | _ ("Failed to start the ARM service: %s\n"), | ||
342 | req_string (rs)); | ||
343 | GNUNET_SCHEDULER_shutdown (); | ||
344 | return; | ||
345 | } | ||
346 | if ((GNUNET_ARM_RESULT_STARTING != result) && | ||
347 | (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result)) | ||
348 | { | ||
349 | fprintf (stdout, | ||
350 | _ ("Failed to start the ARM service: %s\n"), | ||
351 | ret_string (result)); | ||
352 | GNUNET_SCHEDULER_shutdown (); | ||
353 | return; | ||
354 | } | ||
355 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
356 | "ARM service [re]start successful\n"); | ||
357 | start = 0; | ||
358 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
359 | NULL); | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * We have requested ARM to be stopped, this function | ||
365 | * is called with the result of the operation. Informs the | ||
366 | * use of the result; on success, we continue with the event | ||
367 | * loop, on failure we terminate the process. | ||
368 | * | ||
369 | * @param cls closure unused | ||
370 | * @param rs what happened to our request | ||
371 | * @param result if the request was processed, this is the result | ||
372 | * according to ARM | ||
373 | */ | ||
374 | static void | ||
375 | stop_callback (void *cls, | ||
376 | enum GNUNET_ARM_RequestStatus rs, | ||
377 | enum GNUNET_ARM_Result result) | ||
378 | { | ||
379 | char *msg; | ||
380 | |||
381 | (void) cls; | ||
382 | op = NULL; | ||
383 | if (GNUNET_ARM_REQUEST_SENT_OK != rs) | ||
384 | { | ||
385 | GNUNET_asprintf (&msg, | ||
386 | "%s", | ||
387 | _ ( | ||
388 | "Failed to send a stop request to the ARM service: %s\n")); | ||
389 | fprintf (stdout, msg, req_string (rs)); | ||
390 | GNUNET_free (msg); | ||
391 | GNUNET_SCHEDULER_shutdown (); | ||
392 | return; | ||
393 | } | ||
394 | if ( (GNUNET_ARM_RESULT_STOPPING != result) && | ||
395 | (GNUNET_ARM_RESULT_STOPPED != result) && | ||
396 | (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) ) | ||
397 | { | ||
398 | fprintf (stdout, | ||
399 | _ ("Failed to stop the ARM service: %s\n"), | ||
400 | ret_string (result)); | ||
401 | GNUNET_SCHEDULER_shutdown (); | ||
402 | return; | ||
403 | } | ||
404 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
405 | "ARM service shutdown successful\n"); | ||
406 | end = 0; | ||
407 | if (restart) | ||
408 | { | ||
409 | restart = 0; | ||
410 | start = 1; | ||
411 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
412 | "Initiating an ARM restart\n"); | ||
413 | } | ||
414 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
415 | NULL); | ||
416 | } | ||
417 | |||
418 | |||
419 | /** | ||
420 | * We have requested a service to be started, this function | ||
421 | * is called with the result of the operation. Informs the | ||
422 | * use of the result; on success, we continue with the event | ||
423 | * loop, on failure we terminate the process. | ||
424 | * | ||
425 | * @param cls closure unused | ||
426 | * @param rs what happened to our request | ||
427 | * @param result if the request was processed, this is the result | ||
428 | * according to ARM | ||
429 | */ | ||
430 | static void | ||
431 | init_callback (void *cls, | ||
432 | enum GNUNET_ARM_RequestStatus rs, | ||
433 | enum GNUNET_ARM_Result result) | ||
434 | { | ||
435 | (void) cls; | ||
436 | op = NULL; | ||
437 | if (GNUNET_ARM_REQUEST_SENT_OK != rs) | ||
438 | { | ||
439 | fprintf (stdout, | ||
440 | _ ("Failed to send a request to start the `%s' service: %s\n"), | ||
441 | init, | ||
442 | req_string (rs)); | ||
443 | GNUNET_SCHEDULER_shutdown (); | ||
444 | return; | ||
445 | } | ||
446 | if ((GNUNET_ARM_RESULT_STARTING != result) && | ||
447 | (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result)) | ||
448 | { | ||
449 | fprintf (stdout, | ||
450 | _ ("Failed to start the `%s' service: %s\n"), | ||
451 | init, | ||
452 | ret_string (result)); | ||
453 | GNUNET_SCHEDULER_shutdown (); | ||
454 | return; | ||
455 | } | ||
456 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
457 | "Service %s [re]started successfully\n", | ||
458 | init); | ||
459 | GNUNET_free (init); | ||
460 | init = NULL; | ||
461 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
462 | NULL); | ||
463 | } | ||
464 | |||
465 | |||
466 | /** | ||
467 | * We have requested a service to be stopped, this function | ||
468 | * is called with the result of the operation. Informs the | ||
469 | * use of the result; on success, we continue with the event | ||
470 | * loop, on failure we terminate the process. | ||
471 | * | ||
472 | * @param cls closure unused | ||
473 | * @param rs what happened to our request | ||
474 | * @param result if the request was processed, this is the result | ||
475 | * according to ARM | ||
476 | */ | ||
477 | static void | ||
478 | term_callback (void *cls, | ||
479 | enum GNUNET_ARM_RequestStatus rs, | ||
480 | enum GNUNET_ARM_Result result) | ||
481 | { | ||
482 | char *msg; | ||
483 | |||
484 | (void) cls; | ||
485 | op = NULL; | ||
486 | if (GNUNET_ARM_REQUEST_SENT_OK != rs) | ||
487 | { | ||
488 | GNUNET_asprintf (&msg, | ||
489 | _ ( | ||
490 | "Failed to send a request to kill the `%s' service: %%s\n"), | ||
491 | term); | ||
492 | fprintf (stdout, | ||
493 | msg, | ||
494 | req_string (rs)); | ||
495 | GNUNET_free (msg); | ||
496 | GNUNET_SCHEDULER_shutdown (); | ||
497 | return; | ||
498 | } | ||
499 | if ( (GNUNET_ARM_RESULT_STOPPED != result) && | ||
500 | (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) ) | ||
501 | { | ||
502 | fprintf (stdout, | ||
503 | _ ("Failed to kill the `%s' service: %s\n"), | ||
504 | term, | ||
505 | ret_string (result)); | ||
506 | GNUNET_SCHEDULER_shutdown (); | ||
507 | return; | ||
508 | } | ||
509 | |||
510 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
511 | "Service %s stopped successfully\n", | ||
512 | term); | ||
513 | GNUNET_free (term); | ||
514 | term = NULL; | ||
515 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
516 | NULL); | ||
517 | } | ||
518 | |||
519 | |||
520 | /** | ||
521 | * Function called with the list of running services. Prints | ||
522 | * the list to stdout, then starts the event loop again. | ||
523 | * Prints an error message and terminates the process on errors. | ||
524 | * | ||
525 | * @param cls closure (unused) | ||
526 | * @param rs request status (success, failure, etc.) | ||
527 | * @param count number of services in the list | ||
528 | * @param list list of services managed by arm | ||
529 | */ | ||
530 | static void | ||
531 | list_callback (void *cls, | ||
532 | enum GNUNET_ARM_RequestStatus rs, | ||
533 | unsigned int count, | ||
534 | const struct GNUNET_ARM_ServiceInfo *list) | ||
535 | { | ||
536 | unsigned int num_stopped = 0; | ||
537 | unsigned int num_started = 0; | ||
538 | unsigned int num_stopping = 0; | ||
539 | unsigned int num_failed = 0; | ||
540 | unsigned int num_finished = 0; | ||
541 | (void) cls; | ||
542 | op = NULL; | ||
543 | if (GNUNET_ARM_REQUEST_SENT_OK != rs) | ||
544 | { | ||
545 | char *msg; | ||
546 | |||
547 | GNUNET_asprintf (&msg, | ||
548 | "%s", | ||
549 | _ ("Failed to request a list of services: %s\n")); | ||
550 | fprintf (stdout, | ||
551 | msg, | ||
552 | req_string (rs)); | ||
553 | GNUNET_free (msg); | ||
554 | ret = 3; | ||
555 | GNUNET_SCHEDULER_shutdown (); | ||
556 | } | ||
557 | if (NULL == list) | ||
558 | { | ||
559 | fprintf (stderr, | ||
560 | "%s", | ||
561 | _ ("Error communicating with ARM. ARM not running?\n")); | ||
562 | GNUNET_SCHEDULER_shutdown (); | ||
563 | ret = 3; | ||
564 | return; | ||
565 | } | ||
566 | for (unsigned int i = 0; i < count; i++) | ||
567 | { | ||
568 | switch (list[i].status) | ||
569 | { | ||
570 | case GNUNET_ARM_SERVICE_STATUS_STOPPED: | ||
571 | num_stopped++; | ||
572 | break; | ||
573 | case GNUNET_ARM_SERVICE_STATUS_FAILED: | ||
574 | num_failed++; | ||
575 | break; | ||
576 | case GNUNET_ARM_SERVICE_STATUS_FINISHED: | ||
577 | num_finished++; | ||
578 | break; | ||
579 | case GNUNET_ARM_SERVICE_STATUS_STARTED: | ||
580 | num_started++; | ||
581 | break; | ||
582 | case GNUNET_ARM_SERVICE_STATUS_STOPPING: | ||
583 | num_stopping++; | ||
584 | fprintf (stdout, | ||
585 | "%s (binary='%s', status=stopping)\n", | ||
586 | list[i].name, | ||
587 | list[i].binary); | ||
588 | break; | ||
589 | default: | ||
590 | GNUNET_break_op (0); | ||
591 | fprintf (stdout, | ||
592 | "%s (binary='%s', status=unknown)\n", | ||
593 | list[i].name, | ||
594 | list[i].binary); | ||
595 | break; | ||
596 | } | ||
597 | } | ||
598 | if (! quiet) | ||
599 | { | ||
600 | if (show_all) | ||
601 | fprintf (stdout, | ||
602 | "%s", | ||
603 | _ ("All services:\n")); | ||
604 | else | ||
605 | fprintf (stdout, | ||
606 | "%s", | ||
607 | _ ("Services (excluding stopped services):\n")); | ||
608 | if (num_stopped || num_failed || num_finished || num_stopping || | ||
609 | num_started) | ||
610 | { | ||
611 | int sep = 0; | ||
612 | fprintf (stdout, "("); | ||
613 | if (0 != num_started) | ||
614 | { | ||
615 | if (sep) | ||
616 | fprintf (stdout, " / "); | ||
617 | fprintf (stdout, | ||
618 | "started: %u", | ||
619 | num_started); | ||
620 | sep = 1; | ||
621 | } | ||
622 | if (0 != num_failed) | ||
623 | { | ||
624 | if (sep) | ||
625 | fprintf (stdout, " / "); | ||
626 | fprintf (stdout, | ||
627 | "failed: %u", | ||
628 | num_failed); | ||
629 | sep = 1; | ||
630 | } | ||
631 | if (0 != num_stopping) | ||
632 | { | ||
633 | if (sep) | ||
634 | fprintf (stdout, " / "); | ||
635 | fprintf (stdout, | ||
636 | "stopping: %u", | ||
637 | num_stopping); | ||
638 | sep = 1; | ||
639 | } | ||
640 | if (0 != num_stopped) | ||
641 | { | ||
642 | if (sep) | ||
643 | fprintf (stdout, " / "); | ||
644 | fprintf (stdout, | ||
645 | "stopped: %u", | ||
646 | num_stopped); | ||
647 | sep = 1; | ||
648 | } | ||
649 | if (0 != num_finished) | ||
650 | { | ||
651 | if (sep) | ||
652 | fprintf (stdout, " / "); | ||
653 | fprintf (stdout, | ||
654 | "finished: %u", | ||
655 | num_finished); | ||
656 | sep = 1; | ||
657 | } | ||
658 | fprintf (stdout, ")\n"); | ||
659 | } | ||
660 | else | ||
661 | { | ||
662 | fprintf (stdout, | ||
663 | "%s", | ||
664 | _ ("(No services configured.)\n")); | ||
665 | } | ||
666 | } | ||
667 | for (unsigned int i = 0; i < count; i++) | ||
668 | { | ||
669 | struct GNUNET_TIME_Relative restart_in; | ||
670 | switch (list[i].status) | ||
671 | { | ||
672 | case GNUNET_ARM_SERVICE_STATUS_STOPPED: | ||
673 | if (show_all) | ||
674 | fprintf (stdout, | ||
675 | "%s (binary='%s', status=stopped)\n", | ||
676 | list[i].name, | ||
677 | list[i].binary); | ||
678 | break; | ||
679 | case GNUNET_ARM_SERVICE_STATUS_FAILED: | ||
680 | restart_in = GNUNET_TIME_absolute_get_remaining (list[i].restart_at); | ||
681 | fprintf (stdout, | ||
682 | "%s (binary='%s', status=failed, exit_status=%d, restart_delay='%s')\n", | ||
683 | list[i].name, | ||
684 | list[i].binary, | ||
685 | list[i].last_exit_status, | ||
686 | GNUNET_STRINGS_relative_time_to_string (restart_in, | ||
687 | GNUNET_YES)); | ||
688 | break; | ||
689 | case GNUNET_ARM_SERVICE_STATUS_FINISHED: | ||
690 | fprintf (stdout, | ||
691 | "%s (binary='%s', status=finished)\n", | ||
692 | list[i].name, | ||
693 | list[i].binary); | ||
694 | break; | ||
695 | case GNUNET_ARM_SERVICE_STATUS_STARTED: | ||
696 | fprintf (stdout, | ||
697 | "%s (binary='%s', status=started)\n", | ||
698 | list[i].name, | ||
699 | list[i].binary); | ||
700 | break; | ||
701 | case GNUNET_ARM_SERVICE_STATUS_STOPPING: | ||
702 | fprintf (stdout, | ||
703 | "%s (binary='%s', status=stopping)\n", | ||
704 | list[i].name, | ||
705 | list[i].binary); | ||
706 | break; | ||
707 | default: | ||
708 | GNUNET_break_op (0); | ||
709 | fprintf (stdout, | ||
710 | "%s (binary='%s', status=unknown)\n", | ||
711 | list[i].name, | ||
712 | list[i].binary); | ||
713 | break; | ||
714 | } | ||
715 | } | ||
716 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
717 | NULL); | ||
718 | } | ||
719 | |||
720 | |||
721 | /** | ||
722 | * Main action loop. Runs the various jobs that we've been asked to | ||
723 | * do, in order. | ||
724 | * | ||
725 | * @param cls closure, unused | ||
726 | */ | ||
727 | static void | ||
728 | action_loop (void *cls) | ||
729 | { | ||
730 | (void) cls; | ||
731 | al_task = NULL; | ||
732 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
733 | "Running requested actions\n"); | ||
734 | while (1) | ||
735 | { | ||
736 | switch (phase++) | ||
737 | { | ||
738 | case 0: | ||
739 | if (NULL != term) | ||
740 | { | ||
741 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
742 | "Termination action\n"); | ||
743 | op = GNUNET_ARM_request_service_stop (h, | ||
744 | term, | ||
745 | &term_callback, | ||
746 | NULL); | ||
747 | return; | ||
748 | } | ||
749 | break; | ||
750 | |||
751 | case 1: | ||
752 | if (end || restart) | ||
753 | { | ||
754 | if (GNUNET_YES != | ||
755 | GNUNET_CLIENT_test (cfg, | ||
756 | "arm")) | ||
757 | { | ||
758 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
759 | "GNUnet not running, cannot stop the peer\n"); | ||
760 | } | ||
761 | else | ||
762 | { | ||
763 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
764 | "End action\n"); | ||
765 | op = GNUNET_ARM_request_service_stop (h, | ||
766 | "arm", | ||
767 | &stop_callback, | ||
768 | NULL); | ||
769 | return; | ||
770 | } | ||
771 | } | ||
772 | break; | ||
773 | |||
774 | case 2: | ||
775 | if (start) | ||
776 | { | ||
777 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
778 | "Start action\n"); | ||
779 | op = | ||
780 | GNUNET_ARM_request_service_start (h, | ||
781 | "arm", | ||
782 | (no_stdout | ||
783 | ? 0 | ||
784 | : GNUNET_OS_INHERIT_STD_OUT) | ||
785 | | (no_stderr | ||
786 | ? 0 | ||
787 | : GNUNET_OS_INHERIT_STD_ERR), | ||
788 | &start_callback, | ||
789 | NULL); | ||
790 | return; | ||
791 | } | ||
792 | break; | ||
793 | |||
794 | case 3: | ||
795 | if (NULL != init) | ||
796 | { | ||
797 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
798 | "Initialization action\n"); | ||
799 | op = GNUNET_ARM_request_service_start (h, | ||
800 | init, | ||
801 | GNUNET_OS_INHERIT_STD_NONE, | ||
802 | &init_callback, | ||
803 | NULL); | ||
804 | return; | ||
805 | } | ||
806 | break; | ||
807 | |||
808 | case 4: | ||
809 | if (list) | ||
810 | { | ||
811 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
812 | "Going to list all running services controlled by ARM.\n"); | ||
813 | op = GNUNET_ARM_request_service_list (h, | ||
814 | &list_callback, | ||
815 | &list); | ||
816 | return; | ||
817 | } | ||
818 | break; | ||
819 | |||
820 | case 5: | ||
821 | if (monitor) | ||
822 | { | ||
823 | if (! quiet) | ||
824 | fprintf (stderr, | ||
825 | _ ("Now only monitoring, press CTRL-C to stop.\n")); | ||
826 | quiet = | ||
827 | 0; /* does not make sense to stay quiet in monitor mode at this time */ | ||
828 | return; /* done with tasks, just monitor */ | ||
829 | } | ||
830 | break; | ||
831 | |||
832 | default: /* last phase */ | ||
833 | GNUNET_SCHEDULER_shutdown (); | ||
834 | return; | ||
835 | } | ||
836 | } | ||
837 | } | ||
838 | |||
839 | |||
840 | /** | ||
841 | * Function called when a service starts or stops. | ||
842 | * | ||
843 | * @param cls closure | ||
844 | * @param service service name | ||
845 | * @param status status of the service | ||
846 | */ | ||
847 | static void | ||
848 | srv_status (void *cls, | ||
849 | const char *service, | ||
850 | enum GNUNET_ARM_ServiceMonitorStatus status) | ||
851 | { | ||
852 | const char *msg; | ||
853 | |||
854 | (void) cls; | ||
855 | switch (status) | ||
856 | { | ||
857 | case GNUNET_ARM_SERVICE_MONITORING_STARTED: | ||
858 | return; /* this should be done silently */ | ||
859 | |||
860 | case GNUNET_ARM_SERVICE_STOPPED: | ||
861 | msg = _ ("Stopped %s.\n"); | ||
862 | break; | ||
863 | |||
864 | case GNUNET_ARM_SERVICE_STARTING: | ||
865 | msg = _ ("Starting %s...\n"); | ||
866 | break; | ||
867 | |||
868 | case GNUNET_ARM_SERVICE_STOPPING: | ||
869 | msg = _ ("Stopping %s...\n"); | ||
870 | break; | ||
871 | |||
872 | default: | ||
873 | msg = NULL; | ||
874 | break; | ||
875 | } | ||
876 | if (! quiet) | ||
877 | { | ||
878 | if (NULL != msg) | ||
879 | fprintf (stderr, | ||
880 | msg, | ||
881 | service); | ||
882 | else | ||
883 | fprintf (stderr, | ||
884 | _ ("Unknown status %u for service %s.\n"), | ||
885 | status, | ||
886 | service); | ||
887 | } | ||
888 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
889 | "Got service %s status %d\n", | ||
890 | service, | ||
891 | (int) status); | ||
892 | } | ||
893 | |||
894 | |||
895 | /** | ||
896 | * Task run on timeout (if -T is given). | ||
897 | */ | ||
898 | static void | ||
899 | timeout_task_cb (void *cls) | ||
900 | { | ||
901 | (void) cls; | ||
902 | timeout_task = NULL; | ||
903 | ret = 2; | ||
904 | GNUNET_SCHEDULER_shutdown (); | ||
905 | } | ||
906 | |||
907 | |||
908 | /** | ||
909 | * Main function that will be run by the scheduler. | ||
910 | * | ||
911 | * @param cls closure | ||
912 | * @param args remaining command-line arguments | ||
913 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
914 | * @param c configuration | ||
915 | */ | ||
916 | static void | ||
917 | run (void *cls, | ||
918 | char *const *args, | ||
919 | const char *cfgfile, | ||
920 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
921 | { | ||
922 | (void) cls; | ||
923 | (void) args; | ||
924 | (void) cfgfile; | ||
925 | cfg = GNUNET_CONFIGURATION_dup (c); | ||
926 | if (GNUNET_OK != | ||
927 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
928 | "PATHS", | ||
929 | "GNUNET_HOME", | ||
930 | &dir)) | ||
931 | { | ||
932 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
933 | "PATHS", | ||
934 | "GNUNET_HOME"); | ||
935 | return; | ||
936 | } | ||
937 | (void) GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
938 | "arm", | ||
939 | "CONFIG", | ||
940 | &config_file); | ||
941 | if (NULL == (h = GNUNET_ARM_connect (cfg, | ||
942 | &conn_status, | ||
943 | NULL))) | ||
944 | return; | ||
945 | if (monitor) | ||
946 | m = GNUNET_ARM_monitor_start (cfg, | ||
947 | &srv_status, | ||
948 | NULL); | ||
949 | al_task = GNUNET_SCHEDULER_add_now (&action_loop, | ||
950 | NULL); | ||
951 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
952 | NULL); | ||
953 | if (0 != timeout.rel_value_us) | ||
954 | timeout_task = | ||
955 | GNUNET_SCHEDULER_add_delayed (timeout, | ||
956 | &timeout_task_cb, | ||
957 | NULL); | ||
958 | } | ||
959 | |||
960 | |||
961 | /** | ||
962 | * The main function to obtain arm from gnunetd. | ||
963 | * | ||
964 | * @param argc number of arguments from the command line | ||
965 | * @param argv command line arguments | ||
966 | * @return 0 ok, 1 on error, 2 on timeout | ||
967 | */ | ||
968 | int | ||
969 | main (int argc, char *const *argv) | ||
970 | { | ||
971 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
972 | GNUNET_GETOPT_option_flag ('e', | ||
973 | "end", | ||
974 | gettext_noop ("stop all GNUnet services"), | ||
975 | &end), | ||
976 | GNUNET_GETOPT_option_string ('i', | ||
977 | "init", | ||
978 | "SERVICE", | ||
979 | gettext_noop ("start a particular service"), | ||
980 | &init), | ||
981 | GNUNET_GETOPT_option_string ('k', | ||
982 | "kill", | ||
983 | "SERVICE", | ||
984 | gettext_noop ("stop a particular service"), | ||
985 | &term), | ||
986 | GNUNET_GETOPT_option_flag ('a', | ||
987 | "all", | ||
988 | gettext_noop ( | ||
989 | "also show stopped services (used with -I)"), | ||
990 | &show_all), | ||
991 | GNUNET_GETOPT_option_flag ('s', | ||
992 | "start", | ||
993 | gettext_noop ( | ||
994 | "start all GNUnet default services"), | ||
995 | &start), | ||
996 | GNUNET_GETOPT_option_flag ('r', | ||
997 | "restart", | ||
998 | gettext_noop ( | ||
999 | "stop and start all GNUnet default services"), | ||
1000 | &restart), | ||
1001 | GNUNET_GETOPT_option_flag ('d', | ||
1002 | "delete", | ||
1003 | gettext_noop ( | ||
1004 | "delete config file and directory on exit"), | ||
1005 | &delete), | ||
1006 | GNUNET_GETOPT_option_flag ('m', | ||
1007 | "monitor", | ||
1008 | gettext_noop ("monitor ARM activities"), | ||
1009 | &monitor), | ||
1010 | GNUNET_GETOPT_option_flag ('q', | ||
1011 | "quiet", | ||
1012 | gettext_noop ("don't print status messages"), | ||
1013 | &quiet), | ||
1014 | GNUNET_GETOPT_option_relative_time ( | ||
1015 | 'T', | ||
1016 | "timeout", | ||
1017 | "DELAY", | ||
1018 | gettext_noop ( | ||
1019 | "exit with error status if operation does not finish after DELAY"), | ||
1020 | &timeout), | ||
1021 | GNUNET_GETOPT_option_flag ('I', | ||
1022 | "info", | ||
1023 | gettext_noop ( | ||
1024 | "list currently running services"), | ||
1025 | &list), | ||
1026 | GNUNET_GETOPT_option_flag ( | ||
1027 | 'O', | ||
1028 | "no-stdout", | ||
1029 | gettext_noop ("don't let gnunet-service-arm inherit standard output"), | ||
1030 | &no_stdout), | ||
1031 | GNUNET_GETOPT_option_flag ( | ||
1032 | 'E', | ||
1033 | "no-stderr", | ||
1034 | gettext_noop ("don't let gnunet-service-arm inherit standard error"), | ||
1035 | &no_stderr), | ||
1036 | GNUNET_GETOPT_OPTION_END | ||
1037 | }; | ||
1038 | int lret; | ||
1039 | |||
1040 | if (GNUNET_OK != | ||
1041 | GNUNET_STRINGS_get_utf8_args (argc, | ||
1042 | argv, | ||
1043 | &argc, | ||
1044 | &argv)) | ||
1045 | return 2; | ||
1046 | if (GNUNET_OK == | ||
1047 | (lret = GNUNET_PROGRAM_run ( | ||
1048 | argc, | ||
1049 | argv, | ||
1050 | "gnunet-arm", | ||
1051 | gettext_noop ( | ||
1052 | "Control services and the Automated Restart Manager (ARM)"), | ||
1053 | options, | ||
1054 | &run, | ||
1055 | NULL))) | ||
1056 | { | ||
1057 | GNUNET_free_nz ((void *) argv); | ||
1058 | return ret; | ||
1059 | } | ||
1060 | GNUNET_free_nz ((void *) argv); | ||
1061 | return lret; | ||
1062 | } | ||
1063 | |||
1064 | |||
1065 | /* end of gnunet-arm.c */ | ||
diff --git a/src/cli/arm/meson.build b/src/cli/arm/meson.build new file mode 100644 index 000000000..a50e47ee9 --- /dev/null +++ b/src/cli/arm/meson.build | |||
@@ -0,0 +1,8 @@ | |||
1 | gnunetarm_src = ['gnunet-arm.c'] | ||
2 | |||
3 | executable ('gnunet-arm', | ||
4 | gnunetarm_src, | ||
5 | dependencies: [libgnunetarm_dep, libgnunetutil_dep], | ||
6 | include_directories: [incdir, configuration_inc], | ||
7 | install: true, | ||
8 | install_dir: get_option('bindir')) | ||
diff --git a/src/cli/cadet/.gitignore b/src/cli/cadet/.gitignore new file mode 100644 index 000000000..bcd7158aa --- /dev/null +++ b/src/cli/cadet/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-cadet | |||
diff --git a/src/cli/cadet/Makefile.am b/src/cli/cadet/Makefile.am new file mode 100644 index 000000000..fc9caa12c --- /dev/null +++ b/src/cli/cadet/Makefile.am | |||
@@ -0,0 +1,26 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | plugindir = $(libdir)/gnunet | ||
14 | |||
15 | AM_CLFAGS = -g | ||
16 | |||
17 | bin_PROGRAMS = \ | ||
18 | gnunet-cadet | ||
19 | |||
20 | gnunet_cadet_SOURCES = \ | ||
21 | gnunet-cadet.c | ||
22 | gnunet_cadet_LDADD = \ | ||
23 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
24 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
25 | gnunet_cadet_LDFLAGS = \ | ||
26 | $(GN_LIBINTL) | ||
diff --git a/src/cli/cadet/gnunet-cadet.c b/src/cli/cadet/gnunet-cadet.c new file mode 100644 index 000000000..c77fb914c --- /dev/null +++ b/src/cli/cadet/gnunet-cadet.c | |||
@@ -0,0 +1,851 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012, 2017, 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 | /** | ||
22 | * @file cadet/gnunet-cadet.c | ||
23 | * @brief Print information about cadet tunnels and peers. | ||
24 | * @author Bartlomiej Polot | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_cadet_service.h" | ||
30 | #include "../../service/cadet/cadet.h" // FIXME Smell: this should not be shared like this. | ||
31 | |||
32 | #define STREAM_BUFFER_SIZE 1024 // Pakets | ||
33 | |||
34 | /** | ||
35 | * Option -P. | ||
36 | */ | ||
37 | static int request_peers; | ||
38 | |||
39 | /** | ||
40 | * Option --peer | ||
41 | */ | ||
42 | static char *peer_id; | ||
43 | |||
44 | /** | ||
45 | * Option -T. | ||
46 | */ | ||
47 | static int request_tunnels; | ||
48 | |||
49 | /** | ||
50 | * Option --connection | ||
51 | */ | ||
52 | static char *conn_id; | ||
53 | |||
54 | /** | ||
55 | * Option --channel | ||
56 | */ | ||
57 | static char *channel_id; | ||
58 | |||
59 | /** | ||
60 | * Port to listen on (-o). | ||
61 | */ | ||
62 | static char *listen_port; | ||
63 | |||
64 | /** | ||
65 | * Request echo service | ||
66 | */ | ||
67 | static int echo; | ||
68 | |||
69 | /** | ||
70 | * Time of last echo request. | ||
71 | */ | ||
72 | static struct GNUNET_TIME_Absolute echo_time; | ||
73 | |||
74 | /** | ||
75 | * Task for next echo request. | ||
76 | */ | ||
77 | static struct GNUNET_SCHEDULER_Task *echo_task; | ||
78 | |||
79 | /** | ||
80 | * Peer to connect to. | ||
81 | */ | ||
82 | static char *target_id; | ||
83 | |||
84 | /** | ||
85 | * Port to connect to | ||
86 | */ | ||
87 | static char *target_port = "default"; | ||
88 | |||
89 | /** | ||
90 | * Cadet handle. | ||
91 | */ | ||
92 | static struct GNUNET_CADET_Handle *mh; | ||
93 | |||
94 | /** | ||
95 | * Our configuration. | ||
96 | */ | ||
97 | static const struct GNUNET_CONFIGURATION_Handle *my_cfg; | ||
98 | |||
99 | /** | ||
100 | * Active get path operation. | ||
101 | */ | ||
102 | static struct GNUNET_CADET_GetPath *gpo; | ||
103 | |||
104 | /** | ||
105 | * Active peer listing operation. | ||
106 | */ | ||
107 | static struct GNUNET_CADET_PeersLister *plo; | ||
108 | |||
109 | /** | ||
110 | * Active tunnel listing operation. | ||
111 | */ | ||
112 | static struct GNUNET_CADET_ListTunnels *tio; | ||
113 | |||
114 | /** | ||
115 | * Channel handle. | ||
116 | */ | ||
117 | static struct GNUNET_CADET_Channel *ch; | ||
118 | |||
119 | /** | ||
120 | * HashCode of the given port string | ||
121 | */ | ||
122 | static struct GNUNET_HashCode porthash; | ||
123 | |||
124 | /** | ||
125 | * Data structure for ongoing reception of incoming virtual circuits. | ||
126 | */ | ||
127 | struct GNUNET_CADET_Port *lp; | ||
128 | |||
129 | /** | ||
130 | * Task for reading from stdin. | ||
131 | */ | ||
132 | static struct GNUNET_SCHEDULER_Task *rd_task; | ||
133 | |||
134 | /** | ||
135 | * Task for main job. | ||
136 | */ | ||
137 | static struct GNUNET_SCHEDULER_Task *job; | ||
138 | |||
139 | static unsigned int sent_pkt; | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Wait for input on STDIO and send it out over the #ch. | ||
144 | */ | ||
145 | static void | ||
146 | listen_stdio (void); | ||
147 | |||
148 | |||
149 | /** | ||
150 | * Convert encryption status to human readable string. | ||
151 | * | ||
152 | * @param status Encryption status. | ||
153 | * | ||
154 | * @return Human readable string. | ||
155 | */ | ||
156 | static const char * | ||
157 | enc_2s (uint16_t status) | ||
158 | { | ||
159 | switch (status) | ||
160 | { | ||
161 | case 0: | ||
162 | return "NULL "; | ||
163 | |||
164 | case 1: | ||
165 | return "KSENT"; | ||
166 | |||
167 | case 2: | ||
168 | return "KRECV"; | ||
169 | |||
170 | case 3: | ||
171 | return "READY"; | ||
172 | |||
173 | default: | ||
174 | return ""; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Convert connection status to human readable string. | ||
181 | * | ||
182 | * @param status Connection status. | ||
183 | * | ||
184 | * @return Human readable string. | ||
185 | */ | ||
186 | static const char * | ||
187 | conn_2s (uint16_t status) | ||
188 | { | ||
189 | switch (status) | ||
190 | { | ||
191 | case 0: | ||
192 | return "NEW "; | ||
193 | |||
194 | case 1: | ||
195 | return "SRCH "; | ||
196 | |||
197 | case 2: | ||
198 | return "WAIT "; | ||
199 | |||
200 | case 3: | ||
201 | return "READY"; | ||
202 | |||
203 | case 4: | ||
204 | return "SHUTD"; | ||
205 | |||
206 | default: | ||
207 | return ""; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | |||
212 | /** | ||
213 | * Task to shut down this application. | ||
214 | * | ||
215 | * @param cls Closure (unused). | ||
216 | */ | ||
217 | static void | ||
218 | shutdown_task (void *cls) | ||
219 | { | ||
220 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n"); | ||
221 | if (NULL != lp) | ||
222 | { | ||
223 | GNUNET_CADET_close_port (lp); | ||
224 | lp = NULL; | ||
225 | } | ||
226 | if (NULL != ch) | ||
227 | { | ||
228 | GNUNET_CADET_channel_destroy (ch); | ||
229 | ch = NULL; | ||
230 | } | ||
231 | if (NULL != gpo) | ||
232 | { | ||
233 | GNUNET_CADET_get_path_cancel (gpo); | ||
234 | gpo = NULL; | ||
235 | } | ||
236 | if (NULL != plo) | ||
237 | { | ||
238 | GNUNET_CADET_list_peers_cancel (plo); | ||
239 | plo = NULL; | ||
240 | } | ||
241 | if (NULL != tio) | ||
242 | { | ||
243 | GNUNET_CADET_list_tunnels_cancel (tio); | ||
244 | tio = NULL; | ||
245 | } | ||
246 | if (NULL != mh) | ||
247 | { | ||
248 | GNUNET_CADET_disconnect (mh); | ||
249 | mh = NULL; | ||
250 | } | ||
251 | if (NULL != rd_task) | ||
252 | { | ||
253 | GNUNET_SCHEDULER_cancel (rd_task); | ||
254 | rd_task = NULL; | ||
255 | } | ||
256 | if (NULL != echo_task) | ||
257 | { | ||
258 | GNUNET_SCHEDULER_cancel (echo_task); | ||
259 | echo_task = NULL; | ||
260 | } | ||
261 | if (NULL != job) | ||
262 | { | ||
263 | GNUNET_SCHEDULER_cancel (job); | ||
264 | job = NULL; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | |||
269 | void | ||
270 | mq_cb (void *cls) | ||
271 | { | ||
272 | listen_stdio (); | ||
273 | } | ||
274 | |||
275 | |||
276 | /** | ||
277 | * Task run in stdio mode, after some data is available at stdin. | ||
278 | * | ||
279 | * @param cls Closure (unused). | ||
280 | */ | ||
281 | static void | ||
282 | read_stdio (void *cls) | ||
283 | { | ||
284 | struct GNUNET_MQ_Envelope *env; | ||
285 | struct GNUNET_MessageHeader *msg; | ||
286 | char buf[60000]; | ||
287 | ssize_t data_size; | ||
288 | |||
289 | rd_task = NULL; | ||
290 | data_size = read (0, buf, 60000); | ||
291 | if (data_size < 1) | ||
292 | { | ||
293 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
294 | "read() returned %s\n", | ||
295 | strerror (errno)); | ||
296 | GNUNET_SCHEDULER_shutdown (); | ||
297 | return; | ||
298 | } | ||
299 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
300 | "Read %u bytes from stdio\n", | ||
301 | (unsigned int) data_size); | ||
302 | env = GNUNET_MQ_msg_extra (msg, data_size, GNUNET_MESSAGE_TYPE_CADET_CLI); | ||
303 | GNUNET_memcpy (&msg[1], buf, data_size); | ||
304 | GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env); | ||
305 | |||
306 | sent_pkt++; | ||
307 | |||
308 | if (GNUNET_NO == echo) | ||
309 | { | ||
310 | // Use MQ's notification if too much data of stdin is pooring in too fast. | ||
311 | if (STREAM_BUFFER_SIZE < sent_pkt) | ||
312 | { | ||
313 | GNUNET_MQ_notify_sent (env, mq_cb, cls); | ||
314 | sent_pkt = 0; | ||
315 | } | ||
316 | else | ||
317 | { | ||
318 | listen_stdio (); | ||
319 | } | ||
320 | } | ||
321 | else | ||
322 | { | ||
323 | echo_time = GNUNET_TIME_absolute_get (); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | |||
328 | /** | ||
329 | * Wait for input on STDIO and send it out over the #ch. | ||
330 | */ | ||
331 | static void | ||
332 | listen_stdio () | ||
333 | { | ||
334 | struct GNUNET_NETWORK_FDSet *rs; | ||
335 | |||
336 | /* FIXME: why use 'rs' here, seems overly complicated... */ | ||
337 | rs = GNUNET_NETWORK_fdset_create (); | ||
338 | GNUNET_NETWORK_fdset_set_native (rs, 0); /* STDIN */ | ||
339 | rd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
340 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
341 | rs, | ||
342 | NULL, | ||
343 | &read_stdio, | ||
344 | NULL); | ||
345 | GNUNET_NETWORK_fdset_destroy (rs); | ||
346 | } | ||
347 | |||
348 | |||
349 | /** | ||
350 | * Function called whenever a channel is destroyed. Should clean up | ||
351 | * any associated state. | ||
352 | * | ||
353 | * It must NOT call #GNUNET_CADET_channel_destroy on the channel. | ||
354 | * | ||
355 | * @param cls closure | ||
356 | * @param channel connection to the other end (henceforth invalid) | ||
357 | */ | ||
358 | static void | ||
359 | channel_ended (void *cls, const struct GNUNET_CADET_Channel *channel) | ||
360 | { | ||
361 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n"); | ||
362 | GNUNET_assert (channel == ch); | ||
363 | ch = NULL; | ||
364 | GNUNET_SCHEDULER_shutdown (); | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Method called whenever another peer has added us to a channel | ||
370 | * the other peer initiated. | ||
371 | * Only called (once) upon reception of data with a message type which was | ||
372 | * subscribed to in #GNUNET_CADET_connect. | ||
373 | * | ||
374 | * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored. | ||
375 | * In this case the handler MUST return NULL. | ||
376 | * | ||
377 | * @param cls closure | ||
378 | * @param channel new handle to the channel | ||
379 | * @param initiator peer that started the channel | ||
380 | * @return initial channel context for the channel, we use @a channel | ||
381 | */ | ||
382 | static void * | ||
383 | channel_incoming (void *cls, | ||
384 | struct GNUNET_CADET_Channel *channel, | ||
385 | const struct GNUNET_PeerIdentity *initiator) | ||
386 | { | ||
387 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
388 | "Incoming connection from %s\n", | ||
389 | GNUNET_i2s_full (initiator)); | ||
390 | GNUNET_assert (NULL == ch); | ||
391 | GNUNET_assert (NULL != lp); | ||
392 | GNUNET_CADET_close_port (lp); | ||
393 | lp = NULL; | ||
394 | ch = channel; | ||
395 | if (GNUNET_NO == echo) | ||
396 | listen_stdio (); | ||
397 | return channel; | ||
398 | } | ||
399 | |||
400 | |||
401 | /** | ||
402 | * @brief Send an echo request to the remote peer. | ||
403 | * | ||
404 | * @param cls Closure (NULL). | ||
405 | */ | ||
406 | static void | ||
407 | send_echo (void *cls) | ||
408 | { | ||
409 | struct GNUNET_MQ_Envelope *env; | ||
410 | struct GNUNET_MessageHeader *msg; | ||
411 | |||
412 | echo_task = NULL; | ||
413 | if (NULL == ch) | ||
414 | return; | ||
415 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_CLI); | ||
416 | GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env); | ||
417 | } | ||
418 | |||
419 | |||
420 | /** | ||
421 | * Check data message sanity. Does nothing so far (all messages are OK). | ||
422 | * | ||
423 | * @param cls Closure (unused). | ||
424 | * @param message The message to check. | ||
425 | * @return #GNUNET_OK to keep the channel open, | ||
426 | * #GNUNET_SYSERR to close it (signal serious error). | ||
427 | */ | ||
428 | static int | ||
429 | check_data (void *cls, const struct GNUNET_MessageHeader *message) | ||
430 | { | ||
431 | return GNUNET_OK; /* all is well-formed */ | ||
432 | } | ||
433 | |||
434 | |||
435 | /** | ||
436 | * Function called whenever a message is received. | ||
437 | * | ||
438 | * Each time the function must call #GNUNET_CADET_receive_done on the channel | ||
439 | * in order to receive the next message. This doesn't need to be immediate: | ||
440 | * can be delayed if some processing is done on the message. | ||
441 | * | ||
442 | * @param cls NULL | ||
443 | * @param message The actual message. | ||
444 | */ | ||
445 | static void | ||
446 | handle_data (void *cls, const struct GNUNET_MessageHeader *message) | ||
447 | { | ||
448 | size_t payload_size = ntohs (message->size) - sizeof(*message); | ||
449 | uint16_t len; | ||
450 | ssize_t done; | ||
451 | uint16_t off; | ||
452 | const char *buf; | ||
453 | |||
454 | GNUNET_CADET_receive_done (ch); | ||
455 | if (GNUNET_YES == echo) | ||
456 | { | ||
457 | if (NULL != listen_port) | ||
458 | { | ||
459 | struct GNUNET_MQ_Envelope *env; | ||
460 | struct GNUNET_MessageHeader *msg; | ||
461 | |||
462 | env = | ||
463 | GNUNET_MQ_msg_extra (msg, payload_size, GNUNET_MESSAGE_TYPE_CADET_CLI); | ||
464 | GNUNET_memcpy (&msg[1], &message[1], payload_size); | ||
465 | GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env); | ||
466 | return; | ||
467 | } | ||
468 | else | ||
469 | { | ||
470 | struct GNUNET_TIME_Relative latency; | ||
471 | |||
472 | latency = GNUNET_TIME_absolute_get_duration (echo_time); | ||
473 | echo_time = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
474 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
475 | "time: %s\n", | ||
476 | GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO)); | ||
477 | echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
478 | &send_echo, | ||
479 | NULL); | ||
480 | } | ||
481 | } | ||
482 | |||
483 | len = ntohs (message->size) - sizeof(*message); | ||
484 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len); | ||
485 | buf = (const char *) &message[1]; | ||
486 | off = 0; | ||
487 | while (off < len) | ||
488 | { | ||
489 | done = write (1, &buf[off], len - off); | ||
490 | if (done <= 0) | ||
491 | { | ||
492 | if (-1 == done) | ||
493 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); | ||
494 | GNUNET_SCHEDULER_shutdown (); | ||
495 | return; | ||
496 | } | ||
497 | off += done; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | |||
502 | /** | ||
503 | * Method called to retrieve information about all peers in CADET, called | ||
504 | * once per peer. | ||
505 | * | ||
506 | * After last peer has been reported, an additional call with NULL is done. | ||
507 | * | ||
508 | * @param cls Closure. | ||
509 | * @param ple information about peer, or NULL on "EOF". | ||
510 | */ | ||
511 | static void | ||
512 | peers_callback (void *cls, const struct GNUNET_CADET_PeerListEntry *ple) | ||
513 | { | ||
514 | if (NULL == ple) | ||
515 | { | ||
516 | plo = NULL; | ||
517 | GNUNET_SCHEDULER_shutdown (); | ||
518 | return; | ||
519 | } | ||
520 | fprintf (stdout, | ||
521 | "%s tunnel: %c, paths: %u\n", | ||
522 | GNUNET_i2s_full (&ple->peer), | ||
523 | ple->have_tunnel ? 'Y' : 'N', | ||
524 | ple->n_paths); | ||
525 | } | ||
526 | |||
527 | |||
528 | /** | ||
529 | * Method called to retrieve information about paths to a specific peer | ||
530 | * known to the service. | ||
531 | * | ||
532 | * @param cls Closure. | ||
533 | * @param ppd path detail | ||
534 | */ | ||
535 | static void | ||
536 | path_callback (void *cls, const struct GNUNET_CADET_PeerPathDetail *ppd) | ||
537 | { | ||
538 | if (NULL == ppd) | ||
539 | { | ||
540 | gpo = NULL; | ||
541 | GNUNET_SCHEDULER_shutdown (); | ||
542 | return; | ||
543 | } | ||
544 | fprintf (stdout, "Path of length %u: ", ppd->path_length); | ||
545 | for (unsigned int i = 0; i < ppd->path_length; i++) | ||
546 | fprintf (stdout, | ||
547 | (i == ppd->target_offset) ? "*%s* " : "%s ", | ||
548 | GNUNET_i2s (&ppd->path[i])); | ||
549 | fprintf (stdout, "\n"); | ||
550 | } | ||
551 | |||
552 | |||
553 | /** | ||
554 | * Method called to retrieve information about all tunnels in CADET. | ||
555 | * | ||
556 | * @param cls Closure. | ||
557 | * @param td tunnel details | ||
558 | */ | ||
559 | static void | ||
560 | tunnels_callback (void *cls, const struct GNUNET_CADET_TunnelDetails *td) | ||
561 | { | ||
562 | if (NULL == td) | ||
563 | { | ||
564 | tio = NULL; | ||
565 | GNUNET_SCHEDULER_shutdown (); | ||
566 | return; | ||
567 | } | ||
568 | fprintf (stdout, | ||
569 | "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n", | ||
570 | GNUNET_i2s_full (&td->peer), | ||
571 | enc_2s (td->estate), | ||
572 | conn_2s (td->cstate), | ||
573 | td->channels, | ||
574 | td->connections); | ||
575 | } | ||
576 | |||
577 | |||
578 | /** | ||
579 | * Call CADET's meta API, get all peers known to a peer. | ||
580 | * | ||
581 | * @param cls Closure (unused). | ||
582 | */ | ||
583 | static void | ||
584 | get_peers (void *cls) | ||
585 | { | ||
586 | job = NULL; | ||
587 | plo = GNUNET_CADET_list_peers (my_cfg, &peers_callback, NULL); | ||
588 | } | ||
589 | |||
590 | |||
591 | /** | ||
592 | * Call CADET's monitor API, get info of one peer. | ||
593 | * | ||
594 | * @param cls Closure (unused). | ||
595 | */ | ||
596 | static void | ||
597 | show_peer (void *cls) | ||
598 | { | ||
599 | struct GNUNET_PeerIdentity pid; | ||
600 | |||
601 | job = NULL; | ||
602 | if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id, | ||
603 | strlen (peer_id), | ||
604 | &pid.public_key)) | ||
605 | { | ||
606 | fprintf (stderr, _ ("Invalid peer ID `%s'\n"), peer_id); | ||
607 | GNUNET_SCHEDULER_shutdown (); | ||
608 | return; | ||
609 | } | ||
610 | gpo = GNUNET_CADET_get_path (my_cfg, &pid, &path_callback, NULL); | ||
611 | } | ||
612 | |||
613 | |||
614 | /** | ||
615 | * Call CADET's meta API, get all tunnels known to a peer. | ||
616 | * | ||
617 | * @param cls Closure (unused). | ||
618 | */ | ||
619 | static void | ||
620 | get_tunnels (void *cls) | ||
621 | { | ||
622 | job = NULL; | ||
623 | tio = GNUNET_CADET_list_tunnels (my_cfg, &tunnels_callback, NULL); | ||
624 | } | ||
625 | |||
626 | |||
627 | /** | ||
628 | * Call CADET's monitor API, get info of one channel. | ||
629 | * | ||
630 | * @param cls Closure (unused). | ||
631 | */ | ||
632 | static void | ||
633 | show_channel (void *cls) | ||
634 | { | ||
635 | job = NULL; | ||
636 | GNUNET_break (0); | ||
637 | } | ||
638 | |||
639 | |||
640 | /** | ||
641 | * Call CADET's monitor API, get info of one connection. | ||
642 | * | ||
643 | * @param cls Closure (unused). | ||
644 | */ | ||
645 | static void | ||
646 | show_connection (void *cls) | ||
647 | { | ||
648 | job = NULL; | ||
649 | GNUNET_break (0); | ||
650 | } | ||
651 | |||
652 | |||
653 | /** | ||
654 | * Main function that will be run by the scheduler. | ||
655 | * | ||
656 | * @param cls closure | ||
657 | * @param args remaining command-line arguments | ||
658 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
659 | * @param cfg configuration | ||
660 | */ | ||
661 | static void | ||
662 | run (void *cls, | ||
663 | char *const *args, | ||
664 | const char *cfgfile, | ||
665 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
666 | { | ||
667 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
668 | { GNUNET_MQ_hd_var_size (data, | ||
669 | GNUNET_MESSAGE_TYPE_CADET_CLI, | ||
670 | struct GNUNET_MessageHeader, | ||
671 | NULL), | ||
672 | GNUNET_MQ_handler_end () }; | ||
673 | |||
674 | /* FIXME add option to monitor apps */ | ||
675 | my_cfg = cfg; | ||
676 | target_id = args[0]; | ||
677 | if (target_id && args[1]) | ||
678 | target_port = args[1]; | ||
679 | |||
680 | if (((0 != (request_peers | request_tunnels)) || (NULL != conn_id) || | ||
681 | (NULL != channel_id) ) && | ||
682 | (target_id != NULL) ) | ||
683 | { | ||
684 | fprintf (stderr, | ||
685 | _ ("Extra arguments are not applicable " | ||
686 | "in combination with this option.\n")); | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | if (NULL != peer_id) | ||
691 | { | ||
692 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show peer\n"); | ||
693 | job = GNUNET_SCHEDULER_add_now (&show_peer, NULL); | ||
694 | } | ||
695 | else if (NULL != channel_id) | ||
696 | { | ||
697 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n"); | ||
698 | job = GNUNET_SCHEDULER_add_now (&show_channel, NULL); | ||
699 | } | ||
700 | else if (NULL != conn_id) | ||
701 | { | ||
702 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n"); | ||
703 | job = GNUNET_SCHEDULER_add_now (&show_connection, NULL); | ||
704 | } | ||
705 | else if (GNUNET_YES == request_peers) | ||
706 | { | ||
707 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n"); | ||
708 | job = GNUNET_SCHEDULER_add_now (&get_peers, NULL); | ||
709 | } | ||
710 | else if (GNUNET_YES == request_tunnels) | ||
711 | { | ||
712 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n"); | ||
713 | job = GNUNET_SCHEDULER_add_now (&get_tunnels, NULL); | ||
714 | } | ||
715 | |||
716 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CADET service\n"); | ||
717 | mh = GNUNET_CADET_connect (cfg); | ||
718 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
719 | if (NULL == mh) | ||
720 | { | ||
721 | GNUNET_SCHEDULER_shutdown (); | ||
722 | return; | ||
723 | } | ||
724 | if (NULL != listen_port) | ||
725 | { | ||
726 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opening CADET listen port\n"); | ||
727 | GNUNET_CRYPTO_hash (listen_port, strlen (listen_port), &porthash); | ||
728 | lp = GNUNET_CADET_open_port (mh, | ||
729 | &porthash, | ||
730 | &channel_incoming, | ||
731 | NULL, | ||
732 | NULL /* window changes */, | ||
733 | &channel_ended, | ||
734 | handlers); | ||
735 | } | ||
736 | if (NULL != target_id) | ||
737 | { | ||
738 | struct GNUNET_PeerIdentity pid; | ||
739 | |||
740 | if (GNUNET_OK != | ||
741 | GNUNET_CRYPTO_eddsa_public_key_from_string (target_id, | ||
742 | strlen (target_id), | ||
743 | &pid.public_key)) | ||
744 | { | ||
745 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
746 | _ ("Invalid target `%s'\n"), | ||
747 | target_id); | ||
748 | GNUNET_SCHEDULER_shutdown (); | ||
749 | return; | ||
750 | } | ||
751 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
752 | "Connecting to `%s:%s'\n", | ||
753 | target_id, | ||
754 | target_port); | ||
755 | GNUNET_CRYPTO_hash (target_port, strlen (target_port), &porthash); | ||
756 | ch = GNUNET_CADET_channel_create (mh, | ||
757 | NULL, | ||
758 | &pid, | ||
759 | &porthash, | ||
760 | NULL /* window changes */, | ||
761 | &channel_ended, | ||
762 | handlers); | ||
763 | if (GNUNET_YES == echo) | ||
764 | { | ||
765 | echo_task = GNUNET_SCHEDULER_add_now (&send_echo, NULL); | ||
766 | } | ||
767 | else | ||
768 | { | ||
769 | listen_stdio (); | ||
770 | } | ||
771 | } | ||
772 | |||
773 | if ((NULL == lp) && (NULL == job) && (NULL == ch)) | ||
774 | { | ||
775 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _ ("No action requested\n")); | ||
776 | GNUNET_SCHEDULER_shutdown (); | ||
777 | return; | ||
778 | } | ||
779 | } | ||
780 | |||
781 | |||
782 | /** | ||
783 | * The main function to obtain peer information. | ||
784 | * | ||
785 | * @param argc number of arguments from the command line | ||
786 | * @param argv command line arguments | ||
787 | * @return 0 ok, 1 on error | ||
788 | */ | ||
789 | int | ||
790 | main (int argc, char *const *argv) | ||
791 | { | ||
792 | int res; | ||
793 | const char helpstr[] = | ||
794 | "Create tunnels and retrieve info about CADET's status."; | ||
795 | struct GNUNET_GETOPT_CommandLineOption options[] = { /* I would use the terminology 'circuit' here... --lynX */ | ||
796 | GNUNET_GETOPT_option_string ( | ||
797 | 'C', | ||
798 | "connection", | ||
799 | "CONNECTION_ID", | ||
800 | gettext_noop ("Provide information about a particular connection"), | ||
801 | &conn_id), | ||
802 | GNUNET_GETOPT_option_flag ('e', | ||
803 | "echo", | ||
804 | gettext_noop ("Activate echo mode"), | ||
805 | &echo), | ||
806 | GNUNET_GETOPT_option_string ( | ||
807 | 'o', | ||
808 | "open-port", | ||
809 | "SHARED_SECRET", | ||
810 | gettext_noop ( | ||
811 | "Listen for connections using a shared secret among sender and recipient"), | ||
812 | &listen_port), | ||
813 | GNUNET_GETOPT_option_string ('p', | ||
814 | "peer", | ||
815 | "PEER_ID", | ||
816 | gettext_noop ( | ||
817 | "Provide information about a patricular peer"), | ||
818 | &peer_id), | ||
819 | GNUNET_GETOPT_option_flag ('P', | ||
820 | "peers", | ||
821 | gettext_noop ( | ||
822 | "Provide information about all peers"), | ||
823 | &request_peers), | ||
824 | GNUNET_GETOPT_option_flag ('T', | ||
825 | "tunnels", | ||
826 | gettext_noop ( | ||
827 | "Provide information about all tunnels"), | ||
828 | &request_tunnels), | ||
829 | GNUNET_GETOPT_OPTION_END | ||
830 | }; | ||
831 | |||
832 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
833 | return 2; | ||
834 | |||
835 | res = GNUNET_PROGRAM_run (argc, | ||
836 | argv, | ||
837 | "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)", | ||
838 | gettext_noop (helpstr), | ||
839 | options, | ||
840 | &run, | ||
841 | NULL); | ||
842 | |||
843 | GNUNET_free_nz ((void *) argv); | ||
844 | |||
845 | if (GNUNET_OK == res) | ||
846 | return 0; | ||
847 | return 1; | ||
848 | } | ||
849 | |||
850 | |||
851 | /* end of gnunet-cadet.c */ | ||
diff --git a/src/cli/cadet/meson.build b/src/cli/cadet/meson.build new file mode 100644 index 000000000..09a1965ac --- /dev/null +++ b/src/cli/cadet/meson.build | |||
@@ -0,0 +1,15 @@ | |||
1 | executable ('gnunet-cadet', | ||
2 | ['gnunet-cadet.c'], | ||
3 | dependencies: [libgnunetcadet_dep, | ||
4 | libgnunetutil_dep, | ||
5 | libgnunetcore_dep, | ||
6 | libgnunetdht_dep, | ||
7 | m_dep, | ||
8 | libgnunetstatistics_dep, | ||
9 | libgnunetpeerstore_dep, | ||
10 | libgnunettransportapplication_dep, | ||
11 | libgnunethello_dep], | ||
12 | include_directories: [incdir, configuration_inc], | ||
13 | install: true, | ||
14 | install_dir: get_option('bindir')) | ||
15 | |||
diff --git a/src/cli/core/.gitignore b/src/cli/core/.gitignore new file mode 100644 index 000000000..95169ed2b --- /dev/null +++ b/src/cli/core/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-core | |||
diff --git a/src/cli/core/Makefile.am b/src/cli/core/Makefile.am new file mode 100644 index 000000000..97abf0db2 --- /dev/null +++ b/src/cli/core/Makefile.am | |||
@@ -0,0 +1,24 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | plugindir = $(libdir)/gnunet | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | if USE_COVERAGE | ||
11 | AM_CFLAGS = --coverage -O0 | ||
12 | XLIB = -lgcov | ||
13 | endif | ||
14 | |||
15 | bin_PROGRAMS = \ | ||
16 | gnunet-core | ||
17 | |||
18 | gnunet_core_SOURCES = \ | ||
19 | gnunet-core.c | ||
20 | gnunet_core_LDADD = \ | ||
21 | $(top_builddir)/src/service/core/libgnunetcore.la \ | ||
22 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
23 | gnunet_core_LDFLAGS = \ | ||
24 | $(GN_LIBINTL) | ||
diff --git a/src/cli/core/gnunet-core.c b/src/cli/core/gnunet-core.c new file mode 100644 index 000000000..00b08eefc --- /dev/null +++ b/src/cli/core/gnunet-core.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011, 2012, 2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-core.c | ||
23 | * @brief Print information about other peers known to CORE. | ||
24 | * @author Nathan Evans | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_core_service.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Option -m. | ||
33 | */ | ||
34 | static int monitor_connections; | ||
35 | |||
36 | /** | ||
37 | * Option -i. | ||
38 | */ | ||
39 | static int show_pid; | ||
40 | |||
41 | /** | ||
42 | * Option -s. | ||
43 | */ | ||
44 | static int show_conns; | ||
45 | |||
46 | /** | ||
47 | * Handle to the CORE monitor. | ||
48 | */ | ||
49 | static struct GNUNET_CORE_MonitorHandle *mh; | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Task run in monitor mode when the user presses CTRL-C to abort. | ||
54 | * Stops monitoring activity. | ||
55 | * | ||
56 | * @param cls NULL | ||
57 | */ | ||
58 | static void | ||
59 | shutdown_task (void *cls) | ||
60 | { | ||
61 | (void) cls; | ||
62 | if (NULL != mh) | ||
63 | { | ||
64 | GNUNET_CORE_monitor_stop (mh); | ||
65 | mh = NULL; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Function called to notify core users that another | ||
72 | * peer changed its state with us. | ||
73 | * | ||
74 | * @param cls closure | ||
75 | * @param peer the peer that changed state | ||
76 | * @param state new state of the peer | ||
77 | * @param timeout timeout for the new state | ||
78 | */ | ||
79 | static void | ||
80 | monitor_cb (void *cls, | ||
81 | const struct GNUNET_PeerIdentity *peer, | ||
82 | enum GNUNET_CORE_KxState state, | ||
83 | struct GNUNET_TIME_Absolute timeout) | ||
84 | { | ||
85 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
86 | const char *now_str; | ||
87 | const char *state_str; | ||
88 | |||
89 | (void) cls; | ||
90 | if (((NULL == peer) || (GNUNET_CORE_KX_ITERATION_FINISHED == state)) && | ||
91 | (GNUNET_NO == monitor_connections)) | ||
92 | { | ||
93 | GNUNET_SCHEDULER_shutdown (); | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | switch (state) | ||
98 | { | ||
99 | case GNUNET_CORE_KX_STATE_DOWN: | ||
100 | /* should never happen, as we immediately send the key */ | ||
101 | state_str = _ ("fresh connection"); | ||
102 | break; | ||
103 | |||
104 | case GNUNET_CORE_KX_STATE_KEY_SENT: | ||
105 | state_str = _ ("key sent"); | ||
106 | break; | ||
107 | |||
108 | case GNUNET_CORE_KX_STATE_KEY_RECEIVED: | ||
109 | state_str = _ ("key received"); | ||
110 | break; | ||
111 | |||
112 | case GNUNET_CORE_KX_STATE_UP: | ||
113 | state_str = _ ("connection established"); | ||
114 | break; | ||
115 | |||
116 | case GNUNET_CORE_KX_STATE_REKEY_SENT: | ||
117 | state_str = _ ("rekeying"); | ||
118 | break; | ||
119 | |||
120 | case GNUNET_CORE_KX_PEER_DISCONNECT: | ||
121 | state_str = _ ("disconnected"); | ||
122 | break; | ||
123 | |||
124 | case GNUNET_CORE_KX_ITERATION_FINISHED: | ||
125 | return; | ||
126 | |||
127 | case GNUNET_CORE_KX_CORE_DISCONNECT: | ||
128 | fprintf (stderr, | ||
129 | "%s\n", | ||
130 | _ ("Connection to CORE service lost (reconnecting)")); | ||
131 | return; | ||
132 | |||
133 | default: | ||
134 | state_str = _ ("unknown state"); | ||
135 | break; | ||
136 | } | ||
137 | now_str = GNUNET_STRINGS_absolute_time_to_string (now); | ||
138 | fprintf (stdout, | ||
139 | _ ("%24s: %-30s %4s (timeout in %6s)\n"), | ||
140 | now_str, | ||
141 | state_str, | ||
142 | GNUNET_i2s (peer), | ||
143 | GNUNET_STRINGS_relative_time_to_string ( | ||
144 | GNUNET_TIME_absolute_get_remaining (timeout), | ||
145 | GNUNET_YES)); | ||
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * Main function that will be run by the scheduler. | ||
151 | * | ||
152 | * @param cls closure | ||
153 | * @param args remaining command-line arguments | ||
154 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
155 | * @param cfg configuration | ||
156 | */ | ||
157 | static void | ||
158 | run (void *cls, | ||
159 | char *const *args, | ||
160 | const char *cfgfile, | ||
161 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
162 | { | ||
163 | struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
164 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
165 | char *keyfile; | ||
166 | (void) cls; | ||
167 | (void) cfgfile; | ||
168 | if (NULL != args[0]) | ||
169 | { | ||
170 | fprintf (stderr, _ ("Invalid command line argument `%s'\n"), args[0]); | ||
171 | return; | ||
172 | } | ||
173 | if (GNUNET_OK != | ||
174 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
175 | "PEER", | ||
176 | "PRIVATE_KEY", | ||
177 | &keyfile)) | ||
178 | { | ||
179 | GNUNET_log ( | ||
180 | GNUNET_ERROR_TYPE_ERROR, | ||
181 | _ ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); | ||
182 | GNUNET_SCHEDULER_shutdown (); | ||
183 | return; | ||
184 | } | ||
185 | if (GNUNET_SYSERR == | ||
186 | GNUNET_CRYPTO_eddsa_key_from_file (keyfile, | ||
187 | GNUNET_YES, | ||
188 | &pk)) | ||
189 | { | ||
190 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
191 | "Failed to read peer's private key!\n"); | ||
192 | GNUNET_SCHEDULER_shutdown (); | ||
193 | GNUNET_free (keyfile); | ||
194 | return; | ||
195 | } | ||
196 | GNUNET_CRYPTO_eddsa_key_get_public (&pk, &pub); | ||
197 | if (show_pid) | ||
198 | fprintf (stdout, | ||
199 | _ ("Current local peer identity: %s\n"), | ||
200 | GNUNET_i2s_full ((struct GNUNET_PeerIdentity*) &pub)); | ||
201 | if (show_conns || monitor_connections) | ||
202 | { | ||
203 | mh = GNUNET_CORE_monitor_start (cfg, &monitor_cb, NULL); | ||
204 | if (NULL == mh) | ||
205 | { | ||
206 | fprintf (stderr, "%s", _ ("Failed to connect to CORE service!\n")); | ||
207 | GNUNET_free (keyfile); | ||
208 | return; | ||
209 | } | ||
210 | } | ||
211 | if (! show_pid && ! show_conns && ! monitor_connections) | ||
212 | { | ||
213 | fprintf (stderr, "%s", _ ("No argument given.\n")); | ||
214 | GNUNET_free (keyfile); | ||
215 | return; | ||
216 | } | ||
217 | GNUNET_free (keyfile); | ||
218 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
219 | } | ||
220 | |||
221 | |||
222 | /** | ||
223 | * The main function to obtain peer information from CORE. | ||
224 | * | ||
225 | * @param argc number of arguments from the command line | ||
226 | * @param argv command line arguments | ||
227 | * @return 0 ok, 1 on error | ||
228 | */ | ||
229 | int | ||
230 | main (int argc, char *const *argv) | ||
231 | { | ||
232 | int res; | ||
233 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
234 | { GNUNET_GETOPT_option_flag ( | ||
235 | 'm', | ||
236 | "monitor", | ||
237 | gettext_noop ( | ||
238 | "provide information about all current connections (continuously)"), | ||
239 | &monitor_connections), | ||
240 | GNUNET_GETOPT_option_flag ( | ||
241 | 'i', | ||
242 | "show-identity", | ||
243 | gettext_noop ( | ||
244 | "Show our current peer identity" | ||
245 | ), | ||
246 | &show_pid), | ||
247 | GNUNET_GETOPT_option_flag ( | ||
248 | 's', | ||
249 | "connection-status", | ||
250 | gettext_noop ( | ||
251 | "Show current connections" | ||
252 | ), | ||
253 | &show_conns), | ||
254 | GNUNET_GETOPT_OPTION_END }; | ||
255 | |||
256 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
257 | return 2; | ||
258 | res = GNUNET_PROGRAM_run (argc, | ||
259 | argv, | ||
260 | "gnunet-core", | ||
261 | gettext_noop ( | ||
262 | "Print information about connected peers."), | ||
263 | options, | ||
264 | &run, | ||
265 | NULL); | ||
266 | |||
267 | GNUNET_free_nz ((void *) argv); | ||
268 | if (GNUNET_OK == res) | ||
269 | return 0; | ||
270 | return 1; | ||
271 | } | ||
272 | |||
273 | |||
274 | /* end of gnunet-core.c */ | ||
diff --git a/src/cli/core/meson.build b/src/cli/core/meson.build new file mode 100644 index 000000000..db246a3c4 --- /dev/null +++ b/src/cli/core/meson.build | |||
@@ -0,0 +1,6 @@ | |||
1 | executable ('gnunet-core', | ||
2 | ['gnunet-core.c'], | ||
3 | dependencies: [libgnunetcore_dep, libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
diff --git a/src/cli/datastore/.gitignore b/src/cli/datastore/.gitignore new file mode 100644 index 000000000..ddb105175 --- /dev/null +++ b/src/cli/datastore/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-datastore | |||
diff --git a/src/cli/datastore/Makefile.am b/src/cli/datastore/Makefile.am new file mode 100644 index 000000000..91098db96 --- /dev/null +++ b/src/cli/datastore/Makefile.am | |||
@@ -0,0 +1,24 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | if USE_COVERAGE | ||
11 | AM_CFLAGS = --coverage -O0 | ||
12 | XLIBS = -lgcov | ||
13 | endif | ||
14 | |||
15 | |||
16 | bin_PROGRAMS = \ | ||
17 | gnunet-datastore | ||
18 | |||
19 | gnunet_datastore_SOURCES = \ | ||
20 | gnunet-datastore.c | ||
21 | gnunet_datastore_LDADD = \ | ||
22 | $(top_builddir)/src/service/datastore/libgnunetdatastore.la \ | ||
23 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
24 | $(GN_LIBINTL) | ||
diff --git a/src/cli/datastore/gnunet-datastore.c b/src/cli/datastore/gnunet-datastore.c new file mode 100644 index 000000000..9a76d1160 --- /dev/null +++ b/src/cli/datastore/gnunet-datastore.c | |||
@@ -0,0 +1,508 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file datastore/gnunet-datastore.c | ||
23 | * @brief tool to manipulate datastores | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include <inttypes.h> | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_datastore_service.h" | ||
30 | |||
31 | GNUNET_NETWORK_STRUCT_BEGIN | ||
32 | |||
33 | struct DataRecord | ||
34 | { | ||
35 | /** | ||
36 | * Number of bytes in the item (NBO). | ||
37 | */ | ||
38 | uint32_t size GNUNET_PACKED; | ||
39 | |||
40 | /** | ||
41 | * Type of the item (NBO) (actually an enum GNUNET_BLOCK_Type) | ||
42 | */ | ||
43 | uint32_t type GNUNET_PACKED; | ||
44 | |||
45 | /** | ||
46 | * Priority of the item (NBO). | ||
47 | */ | ||
48 | uint32_t priority GNUNET_PACKED; | ||
49 | |||
50 | /** | ||
51 | * Desired anonymity level (NBO). | ||
52 | */ | ||
53 | uint32_t anonymity GNUNET_PACKED; | ||
54 | |||
55 | /** | ||
56 | * Desired replication level (NBO). | ||
57 | */ | ||
58 | uint32_t replication GNUNET_PACKED; | ||
59 | |||
60 | /** | ||
61 | * Expiration time (NBO). | ||
62 | */ | ||
63 | struct GNUNET_TIME_AbsoluteNBO expiration; | ||
64 | |||
65 | /** | ||
66 | * Key under which the item can be found. | ||
67 | */ | ||
68 | struct GNUNET_HashCode key; | ||
69 | }; | ||
70 | GNUNET_NETWORK_STRUCT_END | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Length of our magic header. | ||
75 | */ | ||
76 | static const size_t MAGIC_LEN = 16; | ||
77 | |||
78 | /** | ||
79 | * Magic header bytes. | ||
80 | */ | ||
81 | static const uint8_t MAGIC_BYTES[16] = "GNUNETDATASTORE1"; | ||
82 | |||
83 | /** | ||
84 | * Dump the database. | ||
85 | */ | ||
86 | static int dump; | ||
87 | |||
88 | /** | ||
89 | * Insert into the database. | ||
90 | */ | ||
91 | static int insert; | ||
92 | |||
93 | /** | ||
94 | * Dump file name. | ||
95 | */ | ||
96 | static char *file_name; | ||
97 | |||
98 | /** | ||
99 | * Dump file handle. | ||
100 | */ | ||
101 | static struct GNUNET_DISK_FileHandle *file_handle; | ||
102 | |||
103 | /** | ||
104 | * Global return value. | ||
105 | */ | ||
106 | static int ret; | ||
107 | |||
108 | /** | ||
109 | * Handle for datastore. | ||
110 | */ | ||
111 | static struct GNUNET_DATASTORE_Handle *datastore; | ||
112 | |||
113 | /** | ||
114 | * Current operation. | ||
115 | */ | ||
116 | static struct GNUNET_DATASTORE_QueueEntry *qe; | ||
117 | |||
118 | /** | ||
119 | * Record count. | ||
120 | */ | ||
121 | static uint64_t record_count; | ||
122 | |||
123 | |||
124 | static void | ||
125 | do_shutdown (void *cls) | ||
126 | { | ||
127 | if (NULL != qe) | ||
128 | GNUNET_DATASTORE_cancel (qe); | ||
129 | if (NULL != datastore) | ||
130 | GNUNET_DATASTORE_disconnect (datastore, GNUNET_NO); | ||
131 | if (NULL != file_handle) | ||
132 | GNUNET_DISK_file_close (file_handle); | ||
133 | } | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Begin dumping the database. | ||
138 | */ | ||
139 | static void | ||
140 | start_dump (void); | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Begin inserting into the database. | ||
145 | */ | ||
146 | static void | ||
147 | start_insert (void); | ||
148 | |||
149 | |||
150 | /** | ||
151 | * Perform next GET operation. | ||
152 | */ | ||
153 | static void | ||
154 | do_get (const uint64_t next_uid); | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Process a datum that was stored in the datastore. | ||
159 | * | ||
160 | * @param cls closure | ||
161 | * @param key key for the content | ||
162 | * @param size number of bytes in data | ||
163 | * @param data content stored | ||
164 | * @param type type of the content | ||
165 | * @param priority priority of the content | ||
166 | * @param anonymity anonymity-level for the content | ||
167 | * @param replication replication-level for the content | ||
168 | * @param expiration expiration time for the content | ||
169 | * @param uid unique identifier for the datum; | ||
170 | * maybe 0 if no unique identifier is available | ||
171 | */ | ||
172 | static void | ||
173 | get_cb (void *cls, | ||
174 | const struct GNUNET_HashCode *key, | ||
175 | size_t size, | ||
176 | const void *data, | ||
177 | enum GNUNET_BLOCK_Type type, | ||
178 | uint32_t priority, | ||
179 | uint32_t anonymity, | ||
180 | uint32_t replication, | ||
181 | struct GNUNET_TIME_Absolute expiration, | ||
182 | uint64_t uid) | ||
183 | { | ||
184 | qe = NULL; | ||
185 | if (NULL == key) | ||
186 | { | ||
187 | fprintf (stderr, _ ("Dumped %" PRIu64 " records\n"), record_count); | ||
188 | GNUNET_DISK_file_close (file_handle); | ||
189 | file_handle = NULL; | ||
190 | if (insert) | ||
191 | start_insert (); | ||
192 | else | ||
193 | { | ||
194 | ret = 0; | ||
195 | GNUNET_SCHEDULER_shutdown (); | ||
196 | } | ||
197 | return; | ||
198 | } | ||
199 | |||
200 | struct DataRecord dr; | ||
201 | dr.size = htonl ((uint32_t) size); | ||
202 | dr.type = htonl (type); | ||
203 | dr.priority = htonl (priority); | ||
204 | dr.anonymity = htonl (anonymity); | ||
205 | dr.replication = htonl (replication); | ||
206 | dr.expiration = GNUNET_TIME_absolute_hton (expiration); | ||
207 | dr.key = *key; | ||
208 | |||
209 | ssize_t len; | ||
210 | len = GNUNET_DISK_file_write (file_handle, &dr, sizeof(dr)); | ||
211 | if (sizeof(dr) != len) | ||
212 | { | ||
213 | fprintf (stderr, | ||
214 | _ ("Short write to file: %zd bytes expecting %zd\n"), | ||
215 | len, | ||
216 | sizeof(dr)); | ||
217 | ret = 1; | ||
218 | GNUNET_SCHEDULER_shutdown (); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | len = GNUNET_DISK_file_write (file_handle, data, size); | ||
223 | if (size != len) | ||
224 | { | ||
225 | fprintf (stderr, | ||
226 | _ ("Short write to file: %zd bytes expecting %zd\n"), | ||
227 | len, | ||
228 | size); | ||
229 | ret = 1; | ||
230 | GNUNET_SCHEDULER_shutdown (); | ||
231 | return; | ||
232 | } | ||
233 | |||
234 | record_count++; | ||
235 | do_get (uid + 1); | ||
236 | } | ||
237 | |||
238 | |||
239 | /** | ||
240 | * Perform next GET operation. | ||
241 | */ | ||
242 | static void | ||
243 | do_get (const uint64_t next_uid) | ||
244 | { | ||
245 | GNUNET_assert (NULL == qe); | ||
246 | qe = GNUNET_DATASTORE_get_key (datastore, | ||
247 | next_uid, | ||
248 | false /* random */, | ||
249 | NULL /* key */, | ||
250 | GNUNET_BLOCK_TYPE_ANY, | ||
251 | 0 /* queue_priority */, | ||
252 | 1 /* max_queue_size */, | ||
253 | &get_cb, | ||
254 | NULL /* proc_cls */); | ||
255 | if (NULL == qe) | ||
256 | { | ||
257 | fprintf (stderr, _ ("Error queueing datastore GET operation\n")); | ||
258 | ret = 1; | ||
259 | GNUNET_SCHEDULER_shutdown (); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Begin dumping the database. | ||
266 | */ | ||
267 | static void | ||
268 | start_dump () | ||
269 | { | ||
270 | record_count = 0; | ||
271 | |||
272 | if (NULL != file_name) | ||
273 | { | ||
274 | file_handle = GNUNET_DISK_file_open (file_name, | ||
275 | GNUNET_DISK_OPEN_WRITE | ||
276 | | GNUNET_DISK_OPEN_TRUNCATE | ||
277 | | GNUNET_DISK_OPEN_CREATE, | ||
278 | GNUNET_DISK_PERM_USER_READ | ||
279 | | GNUNET_DISK_PERM_USER_WRITE); | ||
280 | if (NULL == file_handle) | ||
281 | { | ||
282 | fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name); | ||
283 | ret = 1; | ||
284 | GNUNET_SCHEDULER_shutdown (); | ||
285 | return; | ||
286 | } | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | file_handle = GNUNET_DISK_get_handle_from_int_fd (STDOUT_FILENO); | ||
291 | } | ||
292 | GNUNET_DISK_file_write (file_handle, MAGIC_BYTES, MAGIC_LEN); | ||
293 | do_get (0); | ||
294 | } | ||
295 | |||
296 | |||
297 | /** | ||
298 | * Continuation called to notify client about result of the | ||
299 | * operation. | ||
300 | * | ||
301 | * @param cls closure | ||
302 | * @param success GNUNET_SYSERR on failure (including timeout/queue drop) | ||
303 | * GNUNET_NO if content was already there | ||
304 | * GNUNET_YES (or other positive value) on success | ||
305 | * @param min_expiration minimum expiration time required for 0-priority content to be stored | ||
306 | * by the datacache at this time, zero for unknown, forever if we have no | ||
307 | * space for 0-priority content | ||
308 | * @param msg NULL on success, otherwise an error message | ||
309 | */ | ||
310 | static void | ||
311 | put_cb (void *cls, | ||
312 | int32_t success, | ||
313 | struct GNUNET_TIME_Absolute min_expiration, | ||
314 | const char *msg) | ||
315 | { | ||
316 | qe = NULL; | ||
317 | if (GNUNET_SYSERR == success) | ||
318 | { | ||
319 | fprintf (stderr, _ ("Failed to store item: %s, aborting\n"), msg); | ||
320 | ret = 1; | ||
321 | GNUNET_SCHEDULER_shutdown (); | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | struct DataRecord dr; | ||
326 | ssize_t len; | ||
327 | |||
328 | len = GNUNET_DISK_file_read (file_handle, &dr, sizeof(dr)); | ||
329 | if (0 == len) | ||
330 | { | ||
331 | fprintf (stderr, _ ("Inserted %" PRIu64 " records\n"), record_count); | ||
332 | ret = 0; | ||
333 | GNUNET_SCHEDULER_shutdown (); | ||
334 | return; | ||
335 | } | ||
336 | else if (sizeof(dr) != len) | ||
337 | { | ||
338 | fprintf (stderr, | ||
339 | _ ("Short read from file: %zd bytes expecting %zd\n"), | ||
340 | len, | ||
341 | sizeof(dr)); | ||
342 | ret = 1; | ||
343 | GNUNET_SCHEDULER_shutdown (); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | const size_t size = ntohl (dr.size); | ||
348 | uint8_t data[size]; | ||
349 | len = GNUNET_DISK_file_read (file_handle, data, size); | ||
350 | if (size != len) | ||
351 | { | ||
352 | fprintf (stderr, | ||
353 | _ ("Short read from file: %zd bytes expecting %zd\n"), | ||
354 | len, | ||
355 | size); | ||
356 | ret = 1; | ||
357 | GNUNET_SCHEDULER_shutdown (); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | record_count++; | ||
362 | qe = GNUNET_DATASTORE_put (datastore, | ||
363 | 0, | ||
364 | &dr.key, | ||
365 | size, | ||
366 | data, | ||
367 | ntohl (dr.type), | ||
368 | ntohl (dr.priority), | ||
369 | ntohl (dr.anonymity), | ||
370 | ntohl (dr.replication), | ||
371 | GNUNET_TIME_absolute_ntoh (dr.expiration), | ||
372 | 0, | ||
373 | 1, | ||
374 | &put_cb, | ||
375 | NULL); | ||
376 | if (NULL == qe) | ||
377 | { | ||
378 | fprintf (stderr, _ ("Error queueing datastore PUT operation\n")); | ||
379 | ret = 1; | ||
380 | GNUNET_SCHEDULER_shutdown (); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * Begin inserting into the database. | ||
387 | */ | ||
388 | static void | ||
389 | start_insert () | ||
390 | { | ||
391 | record_count = 0; | ||
392 | |||
393 | if (NULL != file_name) | ||
394 | { | ||
395 | file_handle = GNUNET_DISK_file_open (file_name, | ||
396 | GNUNET_DISK_OPEN_READ, | ||
397 | GNUNET_DISK_PERM_NONE); | ||
398 | if (NULL == file_handle) | ||
399 | { | ||
400 | fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name); | ||
401 | ret = 1; | ||
402 | GNUNET_SCHEDULER_shutdown (); | ||
403 | return; | ||
404 | } | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | file_handle = GNUNET_DISK_get_handle_from_int_fd (STDIN_FILENO); | ||
409 | } | ||
410 | |||
411 | uint8_t buf[MAGIC_LEN]; | ||
412 | ssize_t len; | ||
413 | |||
414 | len = GNUNET_DISK_file_read (file_handle, buf, MAGIC_LEN); | ||
415 | if ((len != MAGIC_LEN) || (0 != memcmp (buf, MAGIC_BYTES, MAGIC_LEN))) | ||
416 | { | ||
417 | fprintf (stderr, _ ("Input file is not of a supported format\n")); | ||
418 | return; | ||
419 | } | ||
420 | put_cb (NULL, GNUNET_YES, GNUNET_TIME_UNIT_ZERO_ABS, NULL); | ||
421 | } | ||
422 | |||
423 | |||
424 | /** | ||
425 | * Main function that will be run by the scheduler. | ||
426 | * | ||
427 | * @param cls closure | ||
428 | * @param args remaining command-line arguments | ||
429 | * @param cfgfile name of the configuration file used | ||
430 | * @param cfg configuration | ||
431 | */ | ||
432 | static void | ||
433 | run (void *cls, | ||
434 | char *const *args, | ||
435 | const char *cfgfile, | ||
436 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
437 | { | ||
438 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
439 | datastore = GNUNET_DATASTORE_connect (cfg); | ||
440 | if (NULL == datastore) | ||
441 | { | ||
442 | fprintf (stderr, _ ("Failed connecting to the datastore.\n")); | ||
443 | ret = 1; | ||
444 | GNUNET_SCHEDULER_shutdown (); | ||
445 | return; | ||
446 | } | ||
447 | if (dump) | ||
448 | start_dump (); | ||
449 | else if (insert) | ||
450 | start_insert (); | ||
451 | else | ||
452 | { | ||
453 | fprintf (stderr, | ||
454 | _ ("Please choose at least one operation: %s, %s\n"), | ||
455 | "dump", | ||
456 | "insert"); | ||
457 | ret = 1; | ||
458 | GNUNET_SCHEDULER_shutdown (); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | |||
463 | /** | ||
464 | * The main function to manipulate datastores. | ||
465 | * | ||
466 | * @param argc number of arguments from the command line | ||
467 | * @param argv command line arguments | ||
468 | * @return 0 ok, 1 on error | ||
469 | */ | ||
470 | int | ||
471 | main (int argc, char *const *argv) | ||
472 | { | ||
473 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
474 | { GNUNET_GETOPT_option_flag ('d', | ||
475 | "dump", | ||
476 | gettext_noop ( | ||
477 | "Dump all records from the datastore"), | ||
478 | &dump), | ||
479 | GNUNET_GETOPT_option_flag ('i', | ||
480 | "insert", | ||
481 | gettext_noop ( | ||
482 | "Insert records into the datastore"), | ||
483 | &insert), | ||
484 | GNUNET_GETOPT_option_filename ('f', | ||
485 | "file", | ||
486 | "FILENAME", | ||
487 | gettext_noop ("File to dump or insert"), | ||
488 | &file_name), | ||
489 | GNUNET_GETOPT_OPTION_END }; | ||
490 | |||
491 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
492 | return 2; | ||
493 | |||
494 | if (GNUNET_OK != | ||
495 | GNUNET_PROGRAM_run (argc, | ||
496 | argv, | ||
497 | "gnunet-datastore", | ||
498 | gettext_noop ("Manipulate GNUnet datastore"), | ||
499 | options, | ||
500 | &run, | ||
501 | NULL)) | ||
502 | ret = 1; | ||
503 | GNUNET_free_nz ((void *) argv); | ||
504 | return ret; | ||
505 | } | ||
506 | |||
507 | |||
508 | /* end of gnunet-datastore.c */ | ||
diff --git a/src/cli/datastore/meson.build b/src/cli/datastore/meson.build new file mode 100644 index 000000000..7ec8eec47 --- /dev/null +++ b/src/cli/datastore/meson.build | |||
@@ -0,0 +1,9 @@ | |||
1 | executable ('gnunet-datastore', | ||
2 | ['gnunet-datastore.c'], | ||
3 | dependencies: [libgnunetdatastore_dep, | ||
4 | libgnunetutil_dep, | ||
5 | libgnunetstatistics_dep, | ||
6 | libgnunetdatacache_dep], | ||
7 | include_directories: [incdir, configuration_inc], | ||
8 | install: true, | ||
9 | install_dir: get_option('bindir')) | ||
diff --git a/src/cli/dht/.gitignore b/src/cli/dht/.gitignore new file mode 100644 index 000000000..3b6564ef0 --- /dev/null +++ b/src/cli/dht/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | gnunet-dht-get | ||
2 | gnunet-dht-monitor | ||
3 | gnunet-dht-profiler | ||
4 | gnunet-dht-put | ||
5 | gnunet-dht-hello | ||
diff --git a/src/cli/dht/Makefile.am b/src/cli/dht/Makefile.am new file mode 100644 index 000000000..f026b5270 --- /dev/null +++ b/src/cli/dht/Makefile.am | |||
@@ -0,0 +1,52 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | if USE_COVERAGE | ||
11 | AM_CFLAGS = --coverage -O0 | ||
12 | XLIB = -lgcov | ||
13 | endif | ||
14 | |||
15 | bin_PROGRAMS = \ | ||
16 | gnunet-dht-monitor \ | ||
17 | gnunet-dht-get \ | ||
18 | gnunet-dht-put \ | ||
19 | gnunet-dht-hello | ||
20 | |||
21 | gnunet_dht_get_SOURCES = \ | ||
22 | gnunet-dht-get.c | ||
23 | gnunet_dht_get_LDADD = \ | ||
24 | $(top_builddir)/src/service/dht/libgnunetdht.la \ | ||
25 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
26 | gnunet_dht_get_LDFLAGS = \ | ||
27 | $(GN_LIBINTL) | ||
28 | |||
29 | gnunet_dht_hello_SOURCES = \ | ||
30 | gnunet-dht-hello.c | ||
31 | gnunet_dht_hello_LDADD = \ | ||
32 | $(top_builddir)/src/service/dht/libgnunetdht.la \ | ||
33 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
34 | gnunet_dht_hello_LDFLAGS = \ | ||
35 | $(GN_LIBINTL) | ||
36 | |||
37 | gnunet_dht_put_SOURCES = \ | ||
38 | gnunet-dht-put.c | ||
39 | gnunet_dht_put_LDADD = \ | ||
40 | $(top_builddir)/src/service/dht/libgnunetdht.la \ | ||
41 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
42 | gnunet_dht_put_LDFLAGS = \ | ||
43 | $(GN_LIBINTL) | ||
44 | |||
45 | gnunet_dht_monitor_SOURCES = \ | ||
46 | gnunet-dht-monitor.c | ||
47 | gnunet_dht_monitor_LDADD = \ | ||
48 | $(top_builddir)/src/service/dht/libgnunetdht.la \ | ||
49 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
50 | gnunet_dht_monitor_LDFLAGS = \ | ||
51 | $(GN_LIBINTL) | ||
52 | |||
diff --git a/src/cli/dht/gnunet-dht-get.c b/src/cli/dht/gnunet-dht-get.c new file mode 100644 index 000000000..393184bb6 --- /dev/null +++ b/src/cli/dht/gnunet-dht-get.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 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 dht/gnunet-dht-get.c | ||
22 | * @brief search for data in DHT | ||
23 | * @author Christian Grothoff | ||
24 | * @author Nathan Evans | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_dht_service.h" | ||
28 | |||
29 | #define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__) | ||
30 | /** | ||
31 | * The type of the query | ||
32 | */ | ||
33 | static unsigned int query_type; | ||
34 | |||
35 | /** | ||
36 | * Desired replication level | ||
37 | */ | ||
38 | static unsigned int replication = 5; | ||
39 | |||
40 | /** | ||
41 | * The key for the query | ||
42 | */ | ||
43 | static char *query_key; | ||
44 | |||
45 | /** | ||
46 | * User supplied timeout value | ||
47 | */ | ||
48 | static struct GNUNET_TIME_Relative timeout_request = { 60000 }; | ||
49 | |||
50 | /** | ||
51 | * Be verbose | ||
52 | */ | ||
53 | static unsigned int verbose; | ||
54 | |||
55 | /** | ||
56 | * Use DHT demultixplex_everywhere | ||
57 | */ | ||
58 | static int demultixplex_everywhere; | ||
59 | |||
60 | /** | ||
61 | * Use #GNUNET_DHT_RO_RECORD_ROUTE. | ||
62 | */ | ||
63 | static int record_route; | ||
64 | |||
65 | /** | ||
66 | * Handle to the DHT | ||
67 | */ | ||
68 | static struct GNUNET_DHT_Handle *dht_handle; | ||
69 | |||
70 | /** | ||
71 | * Global handle of the configuration | ||
72 | */ | ||
73 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
74 | |||
75 | /** | ||
76 | * Handle for the get request | ||
77 | */ | ||
78 | static struct GNUNET_DHT_GetHandle *get_handle; | ||
79 | |||
80 | /** | ||
81 | * Count of results found | ||
82 | */ | ||
83 | static unsigned int result_count; | ||
84 | |||
85 | /** | ||
86 | * Global status value | ||
87 | */ | ||
88 | static int ret; | ||
89 | |||
90 | /** | ||
91 | * Task scheduled to handle timeout. | ||
92 | */ | ||
93 | static struct GNUNET_SCHEDULER_Task *tt; | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Task run to clean up on shutdown. | ||
98 | * | ||
99 | * @param cls unused | ||
100 | */ | ||
101 | static void | ||
102 | cleanup_task (void *cls) | ||
103 | { | ||
104 | if (NULL != get_handle) | ||
105 | { | ||
106 | GNUNET_DHT_get_stop (get_handle); | ||
107 | get_handle = NULL; | ||
108 | } | ||
109 | if (NULL != dht_handle) | ||
110 | { | ||
111 | GNUNET_DHT_disconnect (dht_handle); | ||
112 | dht_handle = NULL; | ||
113 | } | ||
114 | if (NULL != tt) | ||
115 | { | ||
116 | GNUNET_SCHEDULER_cancel (tt); | ||
117 | tt = NULL; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Task run on timeout. Triggers shutdown. | ||
124 | * | ||
125 | * @param cls unused | ||
126 | */ | ||
127 | static void | ||
128 | timeout_task (void *cls) | ||
129 | { | ||
130 | tt = NULL; | ||
131 | GNUNET_SCHEDULER_shutdown (); | ||
132 | } | ||
133 | |||
134 | |||
135 | /** | ||
136 | * Iterator called on each result obtained for a DHT | ||
137 | * operation that expects a reply | ||
138 | * | ||
139 | * @param cls closure | ||
140 | * @param exp when will this value expire | ||
141 | * @param key key of the result | ||
142 | * @param trunc_peer peer at which the path was truncated, or NULL if not | ||
143 | * @param get_path peers on reply path (or NULL if not recorded) | ||
144 | * @param get_path_length number of entries in get_path | ||
145 | * @param put_path peers on the PUT path (or NULL if not recorded) | ||
146 | * @param put_path_length number of entries in get_path | ||
147 | * @param type type of the result | ||
148 | * @param size number of bytes in data | ||
149 | * @param data pointer to the result data | ||
150 | */ | ||
151 | static void | ||
152 | get_result_iterator (void *cls, | ||
153 | struct GNUNET_TIME_Absolute exp, | ||
154 | const struct GNUNET_HashCode *key, | ||
155 | const struct GNUNET_PeerIdentity *trunc_peer, | ||
156 | const struct GNUNET_DHT_PathElement *get_path, | ||
157 | unsigned int get_path_length, | ||
158 | const struct GNUNET_DHT_PathElement *put_path, | ||
159 | unsigned int put_path_length, | ||
160 | enum GNUNET_BLOCK_Type type, | ||
161 | size_t size, | ||
162 | const void *data) | ||
163 | { | ||
164 | fprintf (stdout, | ||
165 | (GNUNET_BLOCK_TYPE_TEST == type) | ||
166 | ? _ ("Result %d, type %d:\n%.*s\n") | ||
167 | : _ ("Result %d, type %d:\n"), | ||
168 | result_count, | ||
169 | type, | ||
170 | (int) size, | ||
171 | (char *) data); | ||
172 | if (record_route && verbose) | ||
173 | { | ||
174 | { | ||
175 | struct GNUNET_PeerIdentity my_identity; | ||
176 | |||
177 | GNUNET_break (GNUNET_OK == | ||
178 | GNUNET_CRYPTO_get_peer_identity (cfg, | ||
179 | &my_identity)); | ||
180 | GNUNET_break (0 == | ||
181 | GNUNET_DHT_verify_path (data, | ||
182 | size, | ||
183 | exp, | ||
184 | trunc_peer, | ||
185 | put_path, | ||
186 | put_path_length, | ||
187 | get_path, | ||
188 | get_path_length, | ||
189 | &my_identity)); | ||
190 | } | ||
191 | fprintf (stdout, | ||
192 | " GET path: "); | ||
193 | for (unsigned int i = 0; i < get_path_length; i++) | ||
194 | fprintf (stdout, | ||
195 | "%s%s", | ||
196 | (0 == i) ? "" : "-", | ||
197 | GNUNET_i2s (&get_path[i].pred)); | ||
198 | fprintf (stdout, | ||
199 | "\n PUT path: "); | ||
200 | for (unsigned int i = 0; i < put_path_length; i++) | ||
201 | fprintf (stdout, | ||
202 | "%s%s", | ||
203 | (0 == i) ? "" : "-", | ||
204 | GNUNET_i2s (&put_path[i].pred)); | ||
205 | if (NULL != trunc_peer) | ||
206 | fprintf (stdout, | ||
207 | "!%s", | ||
208 | GNUNET_i2s (trunc_peer)); | ||
209 | fprintf (stdout, | ||
210 | "\n"); | ||
211 | } | ||
212 | result_count++; | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Main function that will be run by the scheduler. | ||
218 | * | ||
219 | * @param cls closure | ||
220 | * @param args remaining command-line arguments | ||
221 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
222 | * @param c configuration | ||
223 | */ | ||
224 | static void | ||
225 | run (void *cls, | ||
226 | char *const *args, | ||
227 | const char *cfgfile, | ||
228 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
229 | { | ||
230 | struct GNUNET_HashCode key; | ||
231 | enum GNUNET_DHT_RouteOption ro; | ||
232 | |||
233 | cfg = c; | ||
234 | if (NULL == query_key) | ||
235 | { | ||
236 | fprintf (stderr, | ||
237 | "%s", | ||
238 | _ ("Must provide key for DHT GET!\n")); | ||
239 | ret = 1; | ||
240 | return; | ||
241 | } | ||
242 | if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1))) | ||
243 | { | ||
244 | fprintf (stderr, | ||
245 | "%s", | ||
246 | _ ("Failed to connect to DHT service!\n")); | ||
247 | ret = 1; | ||
248 | return; | ||
249 | } | ||
250 | if (query_type == GNUNET_BLOCK_TYPE_ANY) /* Type of data not set */ | ||
251 | query_type = GNUNET_BLOCK_TYPE_TEST; | ||
252 | GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key); | ||
253 | if (verbose) | ||
254 | fprintf (stderr, | ||
255 | "%s `%s' \n", | ||
256 | _ ("Issuing DHT GET with key"), | ||
257 | GNUNET_h2s_full (&key)); | ||
258 | GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); | ||
259 | tt = GNUNET_SCHEDULER_add_delayed (timeout_request, &timeout_task, NULL); | ||
260 | ro = GNUNET_DHT_RO_NONE; | ||
261 | if (demultixplex_everywhere) | ||
262 | ro |= GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE; | ||
263 | if (record_route) | ||
264 | ro |= GNUNET_DHT_RO_RECORD_ROUTE; | ||
265 | get_handle = GNUNET_DHT_get_start (dht_handle, | ||
266 | query_type, | ||
267 | &key, | ||
268 | replication, | ||
269 | ro, | ||
270 | NULL, | ||
271 | 0, | ||
272 | &get_result_iterator, | ||
273 | NULL); | ||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Entry point for gnunet-dht-get | ||
279 | * | ||
280 | * @param argc number of arguments from the command line | ||
281 | * @param argv command line arguments | ||
282 | * @return 0 ok, 1 on error | ||
283 | */ | ||
284 | int | ||
285 | main (int argc, char *const *argv) | ||
286 | { | ||
287 | char *u8_argv = NULL; | ||
288 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
289 | GNUNET_GETOPT_option_string ( | ||
290 | 'k', | ||
291 | "key", | ||
292 | "KEY", | ||
293 | gettext_noop ("the query key"), | ||
294 | &query_key), | ||
295 | GNUNET_GETOPT_option_uint ( | ||
296 | 'r', | ||
297 | "replication", | ||
298 | "LEVEL", | ||
299 | gettext_noop ("how many parallel requests (replicas) to create"), | ||
300 | &replication), | ||
301 | GNUNET_GETOPT_option_flag ( | ||
302 | 'R', | ||
303 | "record", | ||
304 | gettext_noop ("use DHT's record route option"), | ||
305 | &record_route), | ||
306 | GNUNET_GETOPT_option_uint ( | ||
307 | 't', | ||
308 | "type", | ||
309 | "TYPE", | ||
310 | gettext_noop ("the type of data to look for"), | ||
311 | &query_type), | ||
312 | GNUNET_GETOPT_option_relative_time ( | ||
313 | 'T', | ||
314 | "timeout", | ||
315 | "TIMEOUT", | ||
316 | gettext_noop ("how long to execute this query before giving up?"), | ||
317 | &timeout_request), | ||
318 | GNUNET_GETOPT_option_flag ( | ||
319 | 'x', | ||
320 | "demultiplex", | ||
321 | gettext_noop ( | ||
322 | "use DHT's demultiplex everywhere option"), | ||
323 | &demultixplex_everywhere), | ||
324 | GNUNET_GETOPT_option_verbose (&verbose), | ||
325 | GNUNET_GETOPT_OPTION_END | ||
326 | }; | ||
327 | |||
328 | |||
329 | if (GNUNET_OK != | ||
330 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
331 | &argc, &argv)) | ||
332 | return 2; | ||
333 | ret = (GNUNET_OK == | ||
334 | GNUNET_PROGRAM_run ( | ||
335 | argc, | ||
336 | argv, | ||
337 | "gnunet-dht-get", | ||
338 | gettext_noop ( | ||
339 | "Issue a GET request to the GNUnet DHT, prints results."), | ||
340 | options, | ||
341 | &run, | ||
342 | NULL)) | ||
343 | ? ret | ||
344 | : 1; | ||
345 | // This is ugly, but meh. The GNUNET_STRINGS_get_utf8_args allows us to do this. | ||
346 | u8_argv = (char*) argv; | ||
347 | GNUNET_free (u8_argv); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | |||
352 | /* end of gnunet-dht-get.c */ | ||
diff --git a/src/cli/dht/gnunet-dht-hello.c b/src/cli/dht/gnunet-dht-hello.c new file mode 100644 index 000000000..369ed5643 --- /dev/null +++ b/src/cli/dht/gnunet-dht-hello.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 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 dht/gnunet-dht-hello.c | ||
22 | * @brief Obtain HELLO from DHT for bootstrapping | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_dht_service.h" | ||
27 | |||
28 | #define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__) | ||
29 | |||
30 | /** | ||
31 | * Handle to the DHT | ||
32 | */ | ||
33 | static struct GNUNET_DHT_Handle *dht_handle; | ||
34 | |||
35 | /** | ||
36 | * Handle to the DHT hello get operation. | ||
37 | */ | ||
38 | static struct GNUNET_DHT_HelloGetHandle *get_hello_handle; | ||
39 | |||
40 | /** | ||
41 | * Global status value | ||
42 | */ | ||
43 | static int global_ret; | ||
44 | |||
45 | |||
46 | /** | ||
47 | * Task run to clean up on shutdown. | ||
48 | * | ||
49 | * @param cls unused | ||
50 | */ | ||
51 | static void | ||
52 | cleanup_task (void *cls) | ||
53 | { | ||
54 | if (NULL != get_hello_handle) | ||
55 | { | ||
56 | GNUNET_DHT_hello_get_cancel (get_hello_handle); | ||
57 | get_hello_handle = NULL; | ||
58 | } | ||
59 | if (NULL != dht_handle) | ||
60 | { | ||
61 | GNUNET_DHT_disconnect (dht_handle); | ||
62 | dht_handle = NULL; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Task run when we are finished. Triggers shutdown. | ||
69 | * | ||
70 | * @param cls unused | ||
71 | */ | ||
72 | static void | ||
73 | hello_done_cb (void *cls) | ||
74 | { | ||
75 | GNUNET_SCHEDULER_shutdown (); | ||
76 | } | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Function called on our HELLO. | ||
81 | * | ||
82 | * @param cls closure | ||
83 | * @param url the HELLO URL | ||
84 | */ | ||
85 | static void | ||
86 | hello_result_cb (void *cls, | ||
87 | const char *url) | ||
88 | { | ||
89 | get_hello_handle = NULL; | ||
90 | fprintf (stdout, | ||
91 | "%s\n", | ||
92 | url); | ||
93 | GNUNET_SCHEDULER_shutdown (); | ||
94 | } | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Main function that will be run by the scheduler. | ||
99 | * | ||
100 | * @param cls closure, NULL | ||
101 | * @param args remaining command-line arguments | ||
102 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
103 | * @param cfg configuration | ||
104 | */ | ||
105 | static void | ||
106 | run (void *cls, | ||
107 | char *const *args, | ||
108 | const char *cfgfile, | ||
109 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
110 | { | ||
111 | (void) cls; | ||
112 | (void) cfgfile; | ||
113 | GNUNET_SCHEDULER_add_shutdown (&cleanup_task, | ||
114 | NULL); | ||
115 | if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, | ||
116 | 1))) | ||
117 | { | ||
118 | fprintf (stderr, | ||
119 | _ ("Failed to connect to DHT service!\n")); | ||
120 | global_ret = EXIT_NOTCONFIGURED; | ||
121 | GNUNET_SCHEDULER_shutdown (); | ||
122 | return; | ||
123 | } | ||
124 | if (NULL == args[0]) | ||
125 | { | ||
126 | get_hello_handle = GNUNET_DHT_hello_get (dht_handle, | ||
127 | &hello_result_cb, | ||
128 | NULL); | ||
129 | GNUNET_break (NULL != get_hello_handle); | ||
130 | } | ||
131 | else | ||
132 | { | ||
133 | GNUNET_DHT_hello_offer (dht_handle, | ||
134 | args[0], | ||
135 | &hello_done_cb, | ||
136 | NULL); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | |||
141 | /** | ||
142 | * Entry point for gnunet-dht-hello | ||
143 | * | ||
144 | * @param argc number of arguments from the command line | ||
145 | * @param argv command line arguments | ||
146 | * @return 0 ok, 1 on error | ||
147 | */ | ||
148 | int | ||
149 | main (int argc, | ||
150 | char *const *argv) | ||
151 | { | ||
152 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
153 | GNUNET_GETOPT_OPTION_END | ||
154 | }; | ||
155 | enum GNUNET_GenericReturnValue iret; | ||
156 | |||
157 | if (GNUNET_OK != | ||
158 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
159 | &argc, &argv)) | ||
160 | return 2; | ||
161 | iret = GNUNET_PROGRAM_run ( | ||
162 | argc, | ||
163 | argv, | ||
164 | "gnunet-dht-hello [URL]", | ||
165 | gettext_noop ( | ||
166 | "Obtain HELLO from DHT or provide HELLO to DHT for bootstrapping"), | ||
167 | options, | ||
168 | &run, | ||
169 | NULL); | ||
170 | if (GNUNET_SYSERR == iret) | ||
171 | return EXIT_FAILURE; | ||
172 | if (GNUNET_NO == iret) | ||
173 | return EXIT_SUCCESS; | ||
174 | return global_ret; | ||
175 | } | ||
176 | |||
177 | |||
178 | /* end of gnunet-dht-hello.c */ | ||
diff --git a/src/cli/dht/gnunet-dht-monitor.c b/src/cli/dht/gnunet-dht-monitor.c new file mode 100644 index 000000000..93ea1284a --- /dev/null +++ b/src/cli/dht/gnunet-dht-monitor.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012 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 dht/gnunet-dht-monitor.c | ||
22 | * @brief search for data in DHT | ||
23 | * @author Christian Grothoff | ||
24 | * @author Bartlomiej Polot | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_dht_service.h" | ||
28 | |||
29 | /** | ||
30 | * The type of the query | ||
31 | */ | ||
32 | static unsigned int block_type; | ||
33 | |||
34 | /** | ||
35 | * The key to be monitored | ||
36 | */ | ||
37 | static char *query_key; | ||
38 | |||
39 | /** | ||
40 | * User supplied timeout value (in seconds) | ||
41 | */ | ||
42 | static struct GNUNET_TIME_Relative timeout_request = { 60000 }; | ||
43 | |||
44 | /** | ||
45 | * Be verbose | ||
46 | */ | ||
47 | static int verbose; | ||
48 | |||
49 | /** | ||
50 | * Handle to the DHT | ||
51 | */ | ||
52 | static struct GNUNET_DHT_Handle *dht_handle; | ||
53 | |||
54 | /** | ||
55 | * Global handle of the configuration | ||
56 | */ | ||
57 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
58 | |||
59 | /** | ||
60 | * Handle for the get request | ||
61 | */ | ||
62 | static struct GNUNET_DHT_MonitorHandle *monitor_handle; | ||
63 | |||
64 | /** | ||
65 | * Count of messages received | ||
66 | */ | ||
67 | static unsigned int result_count; | ||
68 | |||
69 | /** | ||
70 | * Global status value | ||
71 | */ | ||
72 | static int ret; | ||
73 | |||
74 | /** | ||
75 | * Task scheduled to handle timeout. | ||
76 | */ | ||
77 | static struct GNUNET_SCHEDULER_Task *tt; | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Stop monitoring request and start shutdown | ||
82 | * | ||
83 | * @param cls closure (unused) | ||
84 | */ | ||
85 | static void | ||
86 | cleanup_task (void *cls) | ||
87 | { | ||
88 | if (verbose) | ||
89 | fprintf (stderr, "%s", "Cleaning up!\n"); | ||
90 | if (NULL != monitor_handle) | ||
91 | { | ||
92 | GNUNET_DHT_monitor_stop (monitor_handle); | ||
93 | monitor_handle = NULL; | ||
94 | } | ||
95 | if (NULL != dht_handle) | ||
96 | { | ||
97 | GNUNET_DHT_disconnect (dht_handle); | ||
98 | dht_handle = NULL; | ||
99 | } | ||
100 | if (NULL != tt) | ||
101 | { | ||
102 | GNUNET_SCHEDULER_cancel (tt); | ||
103 | tt = NULL; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | |||
108 | /** | ||
109 | * We hit a timeout. Stop monitoring request and start shutdown | ||
110 | * | ||
111 | * @param cls closure (unused) | ||
112 | */ | ||
113 | static void | ||
114 | timeout_task (void *cls) | ||
115 | { | ||
116 | tt = NULL; | ||
117 | GNUNET_SCHEDULER_shutdown (); | ||
118 | } | ||
119 | |||
120 | |||
121 | /** | ||
122 | * Callback called on each GET request going through the DHT. | ||
123 | * | ||
124 | * @param cls Closure. | ||
125 | * @param options Options, for instance RecordRoute, DemultiplexEverywhere. | ||
126 | * @param type The type of data in the request. | ||
127 | * @param hop_count Hop count so far. | ||
128 | * @param desired_replication_level Desired replication level. | ||
129 | * @param key Key of the requested data. | ||
130 | */ | ||
131 | static void | ||
132 | get_callback (void *cls, | ||
133 | enum GNUNET_DHT_RouteOption options, | ||
134 | enum GNUNET_BLOCK_Type type, | ||
135 | uint32_t hop_count, | ||
136 | uint32_t desired_replication_level, | ||
137 | const struct GNUNET_HashCode *key) | ||
138 | { | ||
139 | fprintf (stdout, | ||
140 | "GET #%u: type %d, key `%s'\n", | ||
141 | result_count, | ||
142 | (int) type, | ||
143 | GNUNET_h2s_full (key)); | ||
144 | result_count++; | ||
145 | } | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Callback called on each GET reply going through the DHT. | ||
150 | * | ||
151 | * @param cls Closure. | ||
152 | * @param type The type of data in the result. | ||
153 | * @param trunc_peer peer where the path was truncated, or NULL if the path is complete | ||
154 | * @param get_path Peers on GET path (or NULL if not recorded). | ||
155 | * @param get_path_length number of entries in get_path. | ||
156 | * @param put_path peers on the PUT path (or NULL if not recorded). | ||
157 | * @param put_path_length number of entries in get_path. | ||
158 | * @param exp Expiration time of the data. | ||
159 | * @param key Key of the data. | ||
160 | * @param data Pointer to the result data. | ||
161 | * @param size Number of bytes in data. | ||
162 | */ | ||
163 | static void | ||
164 | get_resp_callback (void *cls, | ||
165 | enum GNUNET_BLOCK_Type type, | ||
166 | const struct GNUNET_PeerIdentity *trunc_peer, | ||
167 | const struct GNUNET_DHT_PathElement *get_path, | ||
168 | unsigned int get_path_length, | ||
169 | const struct GNUNET_DHT_PathElement *put_path, | ||
170 | unsigned int put_path_length, | ||
171 | struct GNUNET_TIME_Absolute exp, | ||
172 | const struct GNUNET_HashCode *key, | ||
173 | const void *data, | ||
174 | size_t size) | ||
175 | { | ||
176 | fprintf (stdout, | ||
177 | (GNUNET_BLOCK_TYPE_TEST == type) | ||
178 | ? "RESPONSE #%u (%s): type %d, key `%s', data `%.*s'\n" | ||
179 | : "RESPONSE #%u (%s): type %d, key `%s'\n", | ||
180 | result_count, | ||
181 | GNUNET_STRINGS_absolute_time_to_string (exp), | ||
182 | (int) type, | ||
183 | GNUNET_h2s_full (key), | ||
184 | (unsigned int) size, | ||
185 | (char *) data); | ||
186 | result_count++; | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Callback called on each PUT request going through the DHT. | ||
192 | * | ||
193 | * @param cls Closure. | ||
194 | * @param options Options, for instance RecordRoute, DemultiplexEverywhere. | ||
195 | * @param type The type of data in the request. | ||
196 | * @param hop_count Hop count so far. | ||
197 | * @param trunc_peer peer where the path was truncated, or NULL if the path is complete | ||
198 | * @param path_length number of entries in path (or 0 if not recorded). | ||
199 | * @param path peers on the PUT path (or NULL if not recorded). | ||
200 | * @param desired_replication_level Desired replication level. | ||
201 | * @param exp Expiration time of the data. | ||
202 | * @param key Key under which data is to be stored. | ||
203 | * @param data Pointer to the data carried. | ||
204 | * @param size Number of bytes in data. | ||
205 | */ | ||
206 | static void | ||
207 | put_callback (void *cls, | ||
208 | enum GNUNET_DHT_RouteOption options, | ||
209 | enum GNUNET_BLOCK_Type type, | ||
210 | uint32_t hop_count, | ||
211 | uint32_t desired_replication_level, | ||
212 | const struct GNUNET_PeerIdentity *trunc_peer, | ||
213 | unsigned int path_length, | ||
214 | const struct GNUNET_DHT_PathElement *path, | ||
215 | struct GNUNET_TIME_Absolute exp, | ||
216 | const struct GNUNET_HashCode *key, | ||
217 | const void *data, | ||
218 | size_t size) | ||
219 | { | ||
220 | fprintf (stdout, | ||
221 | (GNUNET_BLOCK_TYPE_TEST == type) | ||
222 | ? "PUT %u (%s): type %d, key `%s', data `%.*s'\n" | ||
223 | : "PUT %u (%s): type %d, key `%s'\n", | ||
224 | result_count, | ||
225 | GNUNET_STRINGS_absolute_time_to_string (exp), | ||
226 | (int) type, | ||
227 | GNUNET_h2s_full (key), | ||
228 | (unsigned int) size, | ||
229 | (char *) data); | ||
230 | result_count++; | ||
231 | } | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Main function that will be run by the scheduler. | ||
236 | * | ||
237 | * @param cls closure | ||
238 | * @param args remaining command-line arguments | ||
239 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
240 | * @param c configuration | ||
241 | */ | ||
242 | static void | ||
243 | run (void *cls, | ||
244 | char *const *args, | ||
245 | const char *cfgfile, | ||
246 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
247 | { | ||
248 | struct GNUNET_HashCode *key; | ||
249 | struct GNUNET_HashCode hc; | ||
250 | |||
251 | cfg = c; | ||
252 | |||
253 | if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1))) | ||
254 | { | ||
255 | fprintf (stderr, "%s", _ ("Failed to connect to DHT service!\n")); | ||
256 | ret = 1; | ||
257 | return; | ||
258 | } | ||
259 | if (GNUNET_BLOCK_TYPE_ANY == block_type) /* Type of data not set */ | ||
260 | block_type = GNUNET_BLOCK_TYPE_TEST; | ||
261 | if (NULL != query_key) | ||
262 | { | ||
263 | key = &hc; | ||
264 | if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (query_key, key)) | ||
265 | GNUNET_CRYPTO_hash (query_key, strlen (query_key), key); | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | key = NULL; | ||
270 | } | ||
271 | if (verbose) | ||
272 | fprintf (stderr, | ||
273 | "Monitoring for %s\n", | ||
274 | GNUNET_STRINGS_relative_time_to_string (timeout_request, | ||
275 | GNUNET_NO)); | ||
276 | tt = GNUNET_SCHEDULER_add_delayed (timeout_request, &timeout_task, NULL); | ||
277 | GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); | ||
278 | monitor_handle = GNUNET_DHT_monitor_start (dht_handle, | ||
279 | block_type, | ||
280 | key, | ||
281 | &get_callback, | ||
282 | &get_resp_callback, | ||
283 | &put_callback, | ||
284 | NULL); | ||
285 | } | ||
286 | |||
287 | |||
288 | /** | ||
289 | * Entry point for gnunet-dht-monitor | ||
290 | * | ||
291 | * @param argc number of arguments from the command line | ||
292 | * @param argv command line arguments | ||
293 | * @return 0 ok, 1 on error | ||
294 | */ | ||
295 | int | ||
296 | main (int argc, char *const *argv) | ||
297 | { | ||
298 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
299 | GNUNET_GETOPT_option_string ('k', | ||
300 | "key", | ||
301 | "KEY", | ||
302 | gettext_noop ("the query key"), | ||
303 | &query_key), | ||
304 | |||
305 | GNUNET_GETOPT_option_uint ('t', | ||
306 | "type", | ||
307 | "TYPE", | ||
308 | gettext_noop ("the type of data to look for"), | ||
309 | &block_type), | ||
310 | |||
311 | GNUNET_GETOPT_option_relative_time ( | ||
312 | 'T', | ||
313 | "timeout", | ||
314 | "TIMEOUT", | ||
315 | gettext_noop ("how long should the monitor command run"), | ||
316 | &timeout_request), | ||
317 | |||
318 | GNUNET_GETOPT_option_flag ('V', | ||
319 | "verbose", | ||
320 | gettext_noop ( | ||
321 | "be verbose (print progress information)"), | ||
322 | &verbose), | ||
323 | |||
324 | GNUNET_GETOPT_OPTION_END | ||
325 | }; | ||
326 | |||
327 | |||
328 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
329 | return 2; | ||
330 | |||
331 | return (GNUNET_OK == | ||
332 | GNUNET_PROGRAM_run (argc, | ||
333 | argv, | ||
334 | "gnunet-dht-monitor", | ||
335 | gettext_noop ( | ||
336 | "Prints all packets that go through the DHT."), | ||
337 | options, | ||
338 | &run, | ||
339 | NULL)) | ||
340 | ? ret | ||
341 | : 1; | ||
342 | } | ||
343 | |||
344 | |||
345 | /* end of gnunet-dht-monitor.c */ | ||
diff --git a/src/cli/dht/gnunet-dht-put.c b/src/cli/dht/gnunet-dht-put.c new file mode 100644 index 000000000..8f8e098e4 --- /dev/null +++ b/src/cli/dht/gnunet-dht-put.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2017 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 dht/gnunet-dht-put.c | ||
22 | * @brief search for data in DHT | ||
23 | * @author Christian Grothoff | ||
24 | * @author Nathan Evans | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_dht_service.h" | ||
28 | |||
29 | /** | ||
30 | * The type of the query | ||
31 | */ | ||
32 | static unsigned int query_type; | ||
33 | |||
34 | /** | ||
35 | * The key used in the DHT | ||
36 | */ | ||
37 | struct GNUNET_HashCode key; | ||
38 | |||
39 | /** | ||
40 | * The key for the query | ||
41 | */ | ||
42 | static char *query_key; | ||
43 | |||
44 | /** | ||
45 | * User supplied expiration value | ||
46 | */ | ||
47 | static struct GNUNET_TIME_Relative expiration; | ||
48 | |||
49 | /** | ||
50 | * Desired replication level. | ||
51 | */ | ||
52 | static unsigned int replication = 5; | ||
53 | |||
54 | /** | ||
55 | * Be verbose | ||
56 | */ | ||
57 | static unsigned int verbose; | ||
58 | |||
59 | /** | ||
60 | * Use #GNUNET_DHT_DEMULTIPLEX_EVERYWHERE. | ||
61 | */ | ||
62 | static int demultixplex_everywhere; | ||
63 | |||
64 | /** | ||
65 | * Use #GNUNET_DHT_RO_RECORD_ROUTE. | ||
66 | */ | ||
67 | static int record_route; | ||
68 | |||
69 | /** | ||
70 | * Handle to the DHT | ||
71 | */ | ||
72 | static struct GNUNET_DHT_Handle *dht_handle; | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Global handle of the configuration | ||
77 | */ | ||
78 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
79 | |||
80 | /** | ||
81 | * Global status value | ||
82 | */ | ||
83 | static int ret; | ||
84 | |||
85 | /** | ||
86 | * The data to insert into the dht | ||
87 | */ | ||
88 | static char *data; | ||
89 | |||
90 | |||
91 | static void | ||
92 | shutdown_task (void *cls) | ||
93 | { | ||
94 | if (NULL != dht_handle) | ||
95 | { | ||
96 | GNUNET_DHT_disconnect (dht_handle); | ||
97 | dht_handle = NULL; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Signature of the main function of a task. | ||
104 | * | ||
105 | * @param cls closure | ||
106 | */ | ||
107 | static void | ||
108 | message_sent_cont (void *cls) | ||
109 | { | ||
110 | GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); | ||
111 | } | ||
112 | |||
113 | |||
114 | /** | ||
115 | * Main function that will be run by the scheduler. | ||
116 | * | ||
117 | * @param cls closure | ||
118 | * @param args remaining command-line arguments | ||
119 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
120 | * @param c configuration | ||
121 | */ | ||
122 | static void | ||
123 | run (void *cls, | ||
124 | char *const *args, | ||
125 | const char *cfgfile, | ||
126 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
127 | { | ||
128 | enum GNUNET_DHT_RouteOption ro; | ||
129 | |||
130 | cfg = c; | ||
131 | if ((NULL == query_key) || (NULL == data)) | ||
132 | { | ||
133 | fprintf (stderr, "%s", _ ("Must provide KEY and DATA for DHT put!\n")); | ||
134 | ret = 1; | ||
135 | return; | ||
136 | } | ||
137 | |||
138 | if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1))) | ||
139 | { | ||
140 | fprintf (stderr, _ ("Could not connect to DHT service!\n")); | ||
141 | ret = 1; | ||
142 | return; | ||
143 | } | ||
144 | if (GNUNET_BLOCK_TYPE_ANY == query_type) /* Type of data not set */ | ||
145 | query_type = GNUNET_BLOCK_TYPE_TEST; | ||
146 | |||
147 | GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key); | ||
148 | |||
149 | if (verbose) | ||
150 | fprintf (stderr, | ||
151 | _ ("Issuing put request for `%s' with data `%s'!\n"), | ||
152 | query_key, | ||
153 | data); | ||
154 | ro = GNUNET_DHT_RO_NONE; | ||
155 | if (demultixplex_everywhere) | ||
156 | ro |= GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE; | ||
157 | if (record_route) | ||
158 | ro |= GNUNET_DHT_RO_RECORD_ROUTE; | ||
159 | GNUNET_DHT_put (dht_handle, | ||
160 | &key, | ||
161 | replication, | ||
162 | ro, | ||
163 | query_type, | ||
164 | strlen (data), | ||
165 | data, | ||
166 | GNUNET_TIME_relative_to_absolute (expiration), | ||
167 | &message_sent_cont, | ||
168 | NULL); | ||
169 | } | ||
170 | |||
171 | |||
172 | /** | ||
173 | * Entry point for gnunet-dht-put | ||
174 | * | ||
175 | * @param argc number of arguments from the command line | ||
176 | * @param argv command line arguments | ||
177 | * @return 0 ok, 1 on error | ||
178 | */ | ||
179 | int | ||
180 | main (int argc, char *const *argv) | ||
181 | { | ||
182 | char *u8_argv = NULL; | ||
183 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
184 | GNUNET_GETOPT_option_string ( | ||
185 | 'd', | ||
186 | "data", | ||
187 | "DATA", | ||
188 | gettext_noop ( | ||
189 | "the data to insert under the key"), | ||
190 | &data), | ||
191 | GNUNET_GETOPT_option_relative_time ( | ||
192 | 'e', | ||
193 | "expiration", | ||
194 | "EXPIRATION", | ||
195 | gettext_noop ("how long to store this entry in the dht (in seconds)"), | ||
196 | &expiration), | ||
197 | GNUNET_GETOPT_option_string ( | ||
198 | 'k', | ||
199 | "key", | ||
200 | "KEY", | ||
201 | gettext_noop ("the query key"), | ||
202 | &query_key), | ||
203 | GNUNET_GETOPT_option_flag ( | ||
204 | 'x', | ||
205 | "demultiplex", | ||
206 | gettext_noop ( | ||
207 | "use DHT's demultiplex everywhere option"), | ||
208 | &demultixplex_everywhere), | ||
209 | GNUNET_GETOPT_option_uint ( | ||
210 | 'r', | ||
211 | "replication", | ||
212 | "LEVEL", | ||
213 | gettext_noop ("how many replicas to create"), | ||
214 | &replication), | ||
215 | GNUNET_GETOPT_option_flag ( | ||
216 | 'R', | ||
217 | "record", | ||
218 | gettext_noop ("use DHT's record route option"), | ||
219 | &record_route), | ||
220 | GNUNET_GETOPT_option_uint ( | ||
221 | 't', | ||
222 | "type", | ||
223 | "TYPE", | ||
224 | gettext_noop ("the type to insert data as"), | ||
225 | &query_type), | ||
226 | GNUNET_GETOPT_option_verbose (&verbose), | ||
227 | GNUNET_GETOPT_OPTION_END | ||
228 | }; | ||
229 | |||
230 | |||
231 | if (GNUNET_OK != | ||
232 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
233 | &argc, &argv)) | ||
234 | return 2; | ||
235 | expiration = GNUNET_TIME_UNIT_HOURS; | ||
236 | ret = (GNUNET_OK == | ||
237 | GNUNET_PROGRAM_run ( | ||
238 | argc, | ||
239 | argv, | ||
240 | "gnunet-dht-put", | ||
241 | gettext_noop ( | ||
242 | "Issue a PUT request to the GNUnet DHT insert DATA under KEY."), | ||
243 | options, | ||
244 | &run, | ||
245 | NULL)) | ||
246 | ? ret | ||
247 | : 1; | ||
248 | // This is ugly, but meh. The GNUNET_STRINGS_get_utf8_args allows us to do this. | ||
249 | u8_argv = (char*) argv; | ||
250 | GNUNET_free (u8_argv); | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | |||
255 | /* end of gnunet-dht-put.c */ | ||
diff --git a/src/cli/dht/meson.build b/src/cli/dht/meson.build new file mode 100644 index 000000000..d8c326513 --- /dev/null +++ b/src/cli/dht/meson.build | |||
@@ -0,0 +1,25 @@ | |||
1 | executable ('gnunet-dht-put', | ||
2 | ['gnunet-dht-put.c'], | ||
3 | dependencies: [libgnunetdht_dep, libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
7 | executable ('gnunet-dht-get', | ||
8 | ['gnunet-dht-get.c'], | ||
9 | dependencies: [libgnunetdht_dep, libgnunetutil_dep], | ||
10 | include_directories: [incdir, configuration_inc], | ||
11 | install: true, | ||
12 | install_dir: get_option('bindir')) | ||
13 | executable ('gnunet-dht-monitor', | ||
14 | ['gnunet-dht-monitor.c'], | ||
15 | dependencies: [libgnunetdht_dep, libgnunetutil_dep], | ||
16 | include_directories: [incdir, configuration_inc], | ||
17 | install: true, | ||
18 | install_dir: get_option('bindir')) | ||
19 | executable ('gnunet-dht-hello', | ||
20 | ['gnunet-dht-hello.c'], | ||
21 | dependencies: [libgnunetdht_dep, libgnunetutil_dep], | ||
22 | include_directories: [incdir, configuration_inc], | ||
23 | install: true, | ||
24 | install_dir: get_option('bindir')) | ||
25 | |||
diff --git a/src/cli/fs/.gitignore b/src/cli/fs/.gitignore new file mode 100644 index 000000000..3ca8908d0 --- /dev/null +++ b/src/cli/fs/.gitignore | |||
@@ -0,0 +1,8 @@ | |||
1 | gnunet-unindex | ||
2 | gnunet-auto-share | ||
3 | gnunet-directory | ||
4 | gnunet-download | ||
5 | gnunet-fs | ||
6 | gnunet-publish | ||
7 | gnunet-search | ||
8 | |||
diff --git a/src/cli/fs/Makefile.am b/src/cli/fs/Makefile.am new file mode 100644 index 000000000..0d489dbe6 --- /dev/null +++ b/src/cli/fs/Makefile.am | |||
@@ -0,0 +1,107 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | bin_PROGRAMS = \ | ||
14 | gnunet-auto-share \ | ||
15 | gnunet-directory \ | ||
16 | gnunet-download \ | ||
17 | gnunet-publish \ | ||
18 | gnunet-search \ | ||
19 | gnunet-fs \ | ||
20 | gnunet-unindex | ||
21 | |||
22 | gnunet_directory_SOURCES = \ | ||
23 | gnunet-directory.c | ||
24 | gnunet_directory_LDADD = \ | ||
25 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
26 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
27 | $(GN_LIBINTL) | ||
28 | |||
29 | if HAVE_LIBEXTRACTOR | ||
30 | gnunet_directory_LDADD += \ | ||
31 | -lextractor | ||
32 | endif | ||
33 | |||
34 | gnunet_fs_SOURCES = \ | ||
35 | gnunet-fs.c | ||
36 | gnunet_fs_LDADD = \ | ||
37 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
38 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
39 | $(GN_LIBINTL) | ||
40 | |||
41 | if HAVE_LIBEXTRACTOR | ||
42 | gnunet_fs_LDADD += \ | ||
43 | -lextractor | ||
44 | endif | ||
45 | |||
46 | gnunet_download_SOURCES = \ | ||
47 | gnunet-download.c | ||
48 | gnunet_download_LDADD = \ | ||
49 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
50 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
51 | $(GN_LIBINTL) | ||
52 | |||
53 | gnunet_publish_SOURCES = \ | ||
54 | gnunet-publish.c | ||
55 | gnunet_publish_LDADD = \ | ||
56 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
57 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
58 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
59 | $(GN_LIBINTL) | ||
60 | |||
61 | if HAVE_LIBEXTRACTOR | ||
62 | gnunet_publish_LDADD += \ | ||
63 | -lextractor | ||
64 | endif | ||
65 | |||
66 | gnunet_auto_share_SOURCES = \ | ||
67 | gnunet-auto-share.c | ||
68 | gnunet_auto_share_LDADD = \ | ||
69 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
70 | $(GN_LIBINTL) | ||
71 | |||
72 | if HAVE_LIBEXTRACTOR | ||
73 | gnunet_auto_share_LDADD += \ | ||
74 | -lextractor | ||
75 | endif | ||
76 | |||
77 | gnunet_helper_fs_publish_SOURCES = \ | ||
78 | gnunet-helper-fs-publish.c | ||
79 | gnunet_helper_fs_publish_LDADD = \ | ||
80 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
81 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
82 | $(GN_LIBINTL) | ||
83 | |||
84 | if HAVE_LIBEXTRACTOR | ||
85 | gnunet_helper_fs_publish_LDADD += \ | ||
86 | -lextractor | ||
87 | endif | ||
88 | |||
89 | gnunet_search_SOURCES = \ | ||
90 | gnunet-search.c | ||
91 | gnunet_search_LDADD = \ | ||
92 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
93 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
94 | $(GN_LIBINTL) | ||
95 | |||
96 | if HAVE_LIBEXTRACTOR | ||
97 | gnunet_search_LDADD += \ | ||
98 | -lextractor | ||
99 | endif | ||
100 | |||
101 | |||
102 | gnunet_unindex_SOURCES = \ | ||
103 | gnunet-unindex.c | ||
104 | gnunet_unindex_LDADD = \ | ||
105 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
106 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
107 | $(GN_LIBINTL) | ||
diff --git a/src/cli/fs/gnunet-auto-share.c b/src/cli/fs/gnunet-auto-share.c new file mode 100644 index 000000000..f91e9d00d --- /dev/null +++ b/src/cli/fs/gnunet-auto-share.c | |||
@@ -0,0 +1,791 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001--2012 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 fs/gnunet-auto-share.c | ||
22 | * @brief automatically publish files on GNUnet | ||
23 | * @author Christian Grothoff | ||
24 | * | ||
25 | * TODO: | ||
26 | * - support loading meta data / keywords from resource file | ||
27 | * - add stability timer (a la buildbot) | ||
28 | */ | ||
29 | #include "platform.h" | ||
30 | #include "gnunet_util_lib.h" | ||
31 | |||
32 | #define MAX_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) | ||
33 | |||
34 | #define MIN_DELAY GNUNET_TIME_UNIT_MINUTES | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Item in our work queue (or in the set of files/directories | ||
39 | * we have successfully published). | ||
40 | */ | ||
41 | struct WorkItem | ||
42 | { | ||
43 | /** | ||
44 | * PENDING Work is kept in a linked list. | ||
45 | */ | ||
46 | struct WorkItem *prev; | ||
47 | |||
48 | /** | ||
49 | * PENDING Work is kept in a linked list. | ||
50 | */ | ||
51 | struct WorkItem *next; | ||
52 | |||
53 | /** | ||
54 | * Filename of the work item. | ||
55 | */ | ||
56 | char *filename; | ||
57 | |||
58 | /** | ||
59 | * Unique identity for this work item (used to detect | ||
60 | * if we need to do the work again). | ||
61 | */ | ||
62 | struct GNUNET_HashCode id; | ||
63 | }; | ||
64 | |||
65 | |||
66 | /** | ||
67 | * Global return value from 'main'. | ||
68 | */ | ||
69 | static int ret; | ||
70 | |||
71 | /** | ||
72 | * Are we running 'verbosely'? | ||
73 | */ | ||
74 | static unsigned int verbose; | ||
75 | |||
76 | /** | ||
77 | * Configuration to use. | ||
78 | */ | ||
79 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
80 | |||
81 | /** | ||
82 | * Name of the configuration file. | ||
83 | */ | ||
84 | static char *cfg_filename; | ||
85 | |||
86 | /** | ||
87 | * Disable extractor option to use for publishing. | ||
88 | */ | ||
89 | static int disable_extractor; | ||
90 | |||
91 | /** | ||
92 | * Disable creation time option to use for publishing. | ||
93 | */ | ||
94 | static int do_disable_creation_time; | ||
95 | |||
96 | /** | ||
97 | * Handle for the main task that does scanning and working. | ||
98 | */ | ||
99 | static struct GNUNET_SCHEDULER_Task *run_task; | ||
100 | |||
101 | /** | ||
102 | * Anonymity level option to use for publishing. | ||
103 | */ | ||
104 | static unsigned int anonymity_level = 1; | ||
105 | |||
106 | /** | ||
107 | * Content priority option to use for publishing. | ||
108 | */ | ||
109 | static unsigned int content_priority = 365; | ||
110 | |||
111 | /** | ||
112 | * Replication level option to use for publishing. | ||
113 | */ | ||
114 | static unsigned int replication_level = 1; | ||
115 | |||
116 | /** | ||
117 | * Top-level directory we monitor to auto-publish. | ||
118 | */ | ||
119 | static const char *dir_name; | ||
120 | |||
121 | /** | ||
122 | * Head of linked list of files still to publish. | ||
123 | */ | ||
124 | static struct WorkItem *work_head; | ||
125 | |||
126 | /** | ||
127 | * Tail of linked list of files still to publish. | ||
128 | */ | ||
129 | static struct WorkItem *work_tail; | ||
130 | |||
131 | /** | ||
132 | * Map from the hash of the filename (!) to a `struct WorkItem` | ||
133 | * that was finished. | ||
134 | */ | ||
135 | static struct GNUNET_CONTAINER_MultiHashMap *work_finished; | ||
136 | |||
137 | /** | ||
138 | * Set to #GNUNET_YES if we are shutting down. | ||
139 | */ | ||
140 | static int do_shutdown; | ||
141 | |||
142 | /** | ||
143 | * Start time of the current round; used to determine how long | ||
144 | * one iteration takes (which influences how fast we schedule | ||
145 | * the next one). | ||
146 | */ | ||
147 | static struct GNUNET_TIME_Absolute start_time; | ||
148 | |||
149 | /** | ||
150 | * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal. | ||
151 | */ | ||
152 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
153 | |||
154 | /** | ||
155 | * Handle to the 'gnunet-publish' process that we executed. | ||
156 | */ | ||
157 | static struct GNUNET_OS_Process *publish_proc; | ||
158 | |||
159 | |||
160 | /** | ||
161 | * Compute the name of the state database file we will use. | ||
162 | */ | ||
163 | static char * | ||
164 | get_state_file () | ||
165 | { | ||
166 | char *ret; | ||
167 | |||
168 | GNUNET_asprintf (&ret, | ||
169 | "%s%s.auto-share", | ||
170 | dir_name, | ||
171 | (DIR_SEPARATOR == dir_name[strlen (dir_name) - 1]) | ||
172 | ? "" | ||
173 | : DIR_SEPARATOR_STR); | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | |||
178 | /** | ||
179 | * Load the set of #work_finished items from disk. | ||
180 | */ | ||
181 | static void | ||
182 | load_state () | ||
183 | { | ||
184 | char *fn; | ||
185 | struct GNUNET_BIO_ReadHandle *rh; | ||
186 | uint32_t n; | ||
187 | struct GNUNET_HashCode id; | ||
188 | struct WorkItem *wi; | ||
189 | char *emsg; | ||
190 | |||
191 | emsg = NULL; | ||
192 | fn = get_state_file (); | ||
193 | rh = GNUNET_BIO_read_open_file (fn); | ||
194 | GNUNET_free (fn); | ||
195 | if (NULL == rh) | ||
196 | return; | ||
197 | fn = NULL; | ||
198 | if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "number of files", | ||
199 | (int32_t *) &n)) | ||
200 | goto error; | ||
201 | while (n-- > 0) | ||
202 | { | ||
203 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
204 | GNUNET_BIO_read_spec_string ("filename", &fn, 1024), | ||
205 | GNUNET_BIO_read_spec_object ("id", &id, sizeof(struct GNUNET_HashCode)), | ||
206 | GNUNET_BIO_read_spec_end (), | ||
207 | }; | ||
208 | if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) | ||
209 | goto error; | ||
210 | wi = GNUNET_new (struct WorkItem); | ||
211 | wi->id = id; | ||
212 | wi->filename = fn; | ||
213 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
214 | "Loaded serialization ID for `%s' is `%s'\n", | ||
215 | wi->filename, | ||
216 | GNUNET_h2s (&id)); | ||
217 | fn = NULL; | ||
218 | GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &id); | ||
219 | GNUNET_break (GNUNET_OK == | ||
220 | GNUNET_CONTAINER_multihashmap_put ( | ||
221 | work_finished, | ||
222 | &id, | ||
223 | wi, | ||
224 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
225 | } | ||
226 | if (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg)) | ||
227 | return; | ||
228 | rh = NULL; | ||
229 | error: | ||
230 | GNUNET_free (fn); | ||
231 | if (NULL != rh) | ||
232 | (void) GNUNET_BIO_read_close (rh, &emsg); | ||
233 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
234 | _ ("Failed to load state: %s\n"), | ||
235 | emsg); | ||
236 | GNUNET_free (emsg); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Write work item from the #work_finished map to the given write handle. | ||
242 | * | ||
243 | * @param cls the `struct GNUNET_BIO_WriteHandle *` | ||
244 | * @param key key of the item in the map (unused) | ||
245 | * @param value the `struct WorkItem` to write | ||
246 | * @return #GNUNET_OK to continue to iterate (if write worked) | ||
247 | */ | ||
248 | static int | ||
249 | write_item (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
250 | { | ||
251 | struct GNUNET_BIO_WriteHandle *wh = cls; | ||
252 | struct WorkItem *wi = value; | ||
253 | |||
254 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
255 | "Saving serialization ID of file `%s' with value `%s'\n", | ||
256 | wi->filename, | ||
257 | GNUNET_h2s (&wi->id)); | ||
258 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
259 | GNUNET_BIO_write_spec_string ("auto-share-write-item-filename", | ||
260 | wi->filename), | ||
261 | GNUNET_BIO_write_spec_object ("id", &wi->id, sizeof(struct | ||
262 | GNUNET_HashCode)), | ||
263 | GNUNET_BIO_write_spec_end (), | ||
264 | }; | ||
265 | if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) | ||
266 | return GNUNET_SYSERR; /* write error, abort iteration */ | ||
267 | return GNUNET_OK; | ||
268 | } | ||
269 | |||
270 | |||
271 | /** | ||
272 | * Save the set of #work_finished items on disk. | ||
273 | */ | ||
274 | static void | ||
275 | save_state () | ||
276 | { | ||
277 | uint32_t n; | ||
278 | struct GNUNET_BIO_WriteHandle *wh; | ||
279 | char *fn; | ||
280 | |||
281 | n = GNUNET_CONTAINER_multihashmap_size (work_finished); | ||
282 | fn = get_state_file (); | ||
283 | wh = GNUNET_BIO_write_open_file (fn); | ||
284 | if (NULL == wh) | ||
285 | { | ||
286 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
287 | _ ("Failed to save state to file %s\n"), | ||
288 | fn); | ||
289 | GNUNET_free (fn); | ||
290 | return; | ||
291 | } | ||
292 | if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "size of state", n)) | ||
293 | { | ||
294 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
295 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
296 | _ ("Failed to save state to file %s\n"), | ||
297 | fn); | ||
298 | GNUNET_free (fn); | ||
299 | return; | ||
300 | } | ||
301 | (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished, &write_item, wh); | ||
302 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
303 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
304 | _ ("Failed to save state to file %s\n"), | ||
305 | fn); | ||
306 | GNUNET_free (fn); | ||
307 | } | ||
308 | |||
309 | |||
310 | /** | ||
311 | * Task run on shutdown. Serializes our current state to disk. | ||
312 | * | ||
313 | * @param cls closure, unused | ||
314 | */ | ||
315 | static void | ||
316 | do_stop_task (void *cls) | ||
317 | { | ||
318 | do_shutdown = GNUNET_YES; | ||
319 | if (NULL != publish_proc) | ||
320 | { | ||
321 | GNUNET_OS_process_kill (publish_proc, SIGKILL); | ||
322 | return; | ||
323 | } | ||
324 | if (NULL != run_task) | ||
325 | { | ||
326 | GNUNET_SCHEDULER_cancel (run_task); | ||
327 | run_task = NULL; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Decide what the next task is (working or scanning) and schedule it. | ||
334 | */ | ||
335 | static void | ||
336 | schedule_next_task (void); | ||
337 | |||
338 | |||
339 | /** | ||
340 | * Task triggered whenever we receive a SIGCHLD (child | ||
341 | * process died). | ||
342 | * | ||
343 | * @param cls the `struct WorkItem` we were working on | ||
344 | */ | ||
345 | static void | ||
346 | maint_child_death (void *cls) | ||
347 | { | ||
348 | struct WorkItem *wi = cls; | ||
349 | struct GNUNET_HashCode key; | ||
350 | enum GNUNET_OS_ProcessStatusType type; | ||
351 | unsigned long code; | ||
352 | int ret; | ||
353 | char c; | ||
354 | const struct GNUNET_DISK_FileHandle *pr; | ||
355 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
356 | |||
357 | run_task = NULL; | ||
358 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
359 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
360 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) | ||
361 | { | ||
362 | /* shutdown scheduled us, someone else will kill child, | ||
363 | we should just try again */ | ||
364 | run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
365 | pr, | ||
366 | &maint_child_death, | ||
367 | wi); | ||
368 | return; | ||
369 | } | ||
370 | /* consume the signal */ | ||
371 | GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c))); | ||
372 | |||
373 | ret = GNUNET_OS_process_status (publish_proc, &type, &code); | ||
374 | GNUNET_assert (GNUNET_SYSERR != ret); | ||
375 | if (GNUNET_NO == ret) | ||
376 | { | ||
377 | /* process still running? Then where did the SIGCHLD come from? | ||
378 | Well, let's declare it spurious (kernel bug?) and keep rolling. | ||
379 | */ | ||
380 | GNUNET_break (0); | ||
381 | run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
382 | pr, | ||
383 | &maint_child_death, | ||
384 | wi); | ||
385 | return; | ||
386 | } | ||
387 | GNUNET_assert (GNUNET_OK == ret); | ||
388 | |||
389 | GNUNET_OS_process_destroy (publish_proc); | ||
390 | publish_proc = NULL; | ||
391 | |||
392 | if (GNUNET_YES == do_shutdown) | ||
393 | { | ||
394 | GNUNET_free (wi->filename); | ||
395 | GNUNET_free (wi); | ||
396 | return; | ||
397 | } | ||
398 | if ((GNUNET_OS_PROCESS_EXITED == type) && (0 == code)) | ||
399 | { | ||
400 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
401 | _ ("Publication of `%s' done\n"), | ||
402 | wi->filename); | ||
403 | GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &key); | ||
404 | GNUNET_break (GNUNET_OK == | ||
405 | GNUNET_CONTAINER_multihashmap_put ( | ||
406 | work_finished, | ||
407 | &key, | ||
408 | wi, | ||
409 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
410 | } | ||
411 | else | ||
412 | { | ||
413 | GNUNET_CONTAINER_DLL_insert_tail (work_head, work_tail, wi); | ||
414 | } | ||
415 | save_state (); | ||
416 | schedule_next_task (); | ||
417 | } | ||
418 | |||
419 | |||
420 | /** | ||
421 | * Signal handler called for SIGCHLD. Triggers the | ||
422 | * respective handler by writing to the trigger pipe. | ||
423 | */ | ||
424 | static void | ||
425 | sighandler_child_death () | ||
426 | { | ||
427 | static char c; | ||
428 | int old_errno = errno; /* back-up errno */ | ||
429 | |||
430 | GNUNET_break ( | ||
431 | 1 == | ||
432 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, | ||
433 | GNUNET_DISK_PIPE_END_WRITE), | ||
434 | &c, | ||
435 | sizeof(c))); | ||
436 | errno = old_errno; /* restore errno */ | ||
437 | } | ||
438 | |||
439 | |||
440 | /** | ||
441 | * Function called to process work items. | ||
442 | * | ||
443 | * @param cls closure, NULL | ||
444 | */ | ||
445 | static void | ||
446 | work (void *cls) | ||
447 | { | ||
448 | static char *argv[14]; | ||
449 | static char anon_level[20]; | ||
450 | static char content_prio[20]; | ||
451 | static char repl_level[20]; | ||
452 | struct WorkItem *wi; | ||
453 | const struct GNUNET_DISK_FileHandle *pr; | ||
454 | int argc; | ||
455 | |||
456 | run_task = NULL; | ||
457 | wi = work_head; | ||
458 | GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi); | ||
459 | argc = 0; | ||
460 | argv[argc++] = "gnunet-publish"; | ||
461 | if (verbose) | ||
462 | argv[argc++] = "-V"; | ||
463 | if (disable_extractor) | ||
464 | argv[argc++] = "-D"; | ||
465 | if (do_disable_creation_time) | ||
466 | argv[argc++] = "-d"; | ||
467 | argv[argc++] = "-c"; | ||
468 | argv[argc++] = cfg_filename; | ||
469 | GNUNET_snprintf (anon_level, sizeof(anon_level), "%u", anonymity_level); | ||
470 | argv[argc++] = "-a"; | ||
471 | argv[argc++] = anon_level; | ||
472 | GNUNET_snprintf (content_prio, sizeof(content_prio), "%u", content_priority); | ||
473 | argv[argc++] = "-p"; | ||
474 | argv[argc++] = content_prio; | ||
475 | GNUNET_snprintf (repl_level, sizeof(repl_level), "%u", replication_level); | ||
476 | argv[argc++] = "-r"; | ||
477 | argv[argc++] = repl_level; | ||
478 | argv[argc++] = wi->filename; | ||
479 | argv[argc] = NULL; | ||
480 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Publishing `%s'\n"), wi->filename); | ||
481 | GNUNET_assert (NULL == publish_proc); | ||
482 | publish_proc = GNUNET_OS_start_process_vap (GNUNET_OS_USE_PIPE_CONTROL, | ||
483 | NULL, | ||
484 | NULL, | ||
485 | NULL, | ||
486 | "gnunet-publish", | ||
487 | argv); | ||
488 | if (NULL == publish_proc) | ||
489 | { | ||
490 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
491 | _ ("Failed to run `%s'\n"), | ||
492 | "gnunet-publish"); | ||
493 | GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi); | ||
494 | run_task = | ||
495 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &work, NULL); | ||
496 | return; | ||
497 | } | ||
498 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
499 | run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
500 | pr, | ||
501 | &maint_child_death, | ||
502 | wi); | ||
503 | } | ||
504 | |||
505 | |||
506 | /** | ||
507 | * Recursively scan the given file/directory structure to determine | ||
508 | * a unique ID that represents the current state of the hierarchy. | ||
509 | * | ||
510 | * @param cls where to store the unique ID we are computing | ||
511 | * @param filename file to scan | ||
512 | * @return #GNUNET_OK (always) | ||
513 | */ | ||
514 | static int | ||
515 | determine_id (void *cls, const char *filename) | ||
516 | { | ||
517 | struct GNUNET_HashCode *id = cls; | ||
518 | struct stat sbuf; | ||
519 | struct GNUNET_HashCode fx[2]; | ||
520 | struct GNUNET_HashCode ft; | ||
521 | |||
522 | if (0 != stat (filename, &sbuf)) | ||
523 | { | ||
524 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename); | ||
525 | return GNUNET_OK; | ||
526 | } | ||
527 | GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]); | ||
528 | if (! S_ISDIR (sbuf.st_mode)) | ||
529 | { | ||
530 | uint64_t fattr[2]; | ||
531 | |||
532 | fattr[0] = GNUNET_htonll (sbuf.st_size); | ||
533 | fattr[0] = GNUNET_htonll (sbuf.st_mtime); | ||
534 | |||
535 | GNUNET_CRYPTO_hash (fattr, sizeof(fattr), &fx[1]); | ||
536 | } | ||
537 | else | ||
538 | { | ||
539 | memset (&fx[1], 1, sizeof(struct GNUNET_HashCode)); | ||
540 | GNUNET_DISK_directory_scan (filename, &determine_id, &fx[1]); | ||
541 | } | ||
542 | /* use hash here to make hierarchical structure distinct from | ||
543 | all files on the same level */ | ||
544 | GNUNET_CRYPTO_hash (fx, sizeof(fx), &ft); | ||
545 | /* use XOR here so that order of the files in the directory | ||
546 | does not matter! */ | ||
547 | GNUNET_CRYPTO_hash_xor (&ft, id, id); | ||
548 | return GNUNET_OK; | ||
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * Function called with a filename (or directory name) to publish | ||
554 | * (if it has changed since the last time we published it). This function | ||
555 | * is called for the top-level files only. | ||
556 | * | ||
557 | * @param cls closure, NULL | ||
558 | * @param filename complete filename (absolute path) | ||
559 | * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR during shutdown | ||
560 | */ | ||
561 | static int | ||
562 | add_file (void *cls, const char *filename) | ||
563 | { | ||
564 | struct WorkItem *wi; | ||
565 | struct GNUNET_HashCode key; | ||
566 | struct GNUNET_HashCode id; | ||
567 | |||
568 | if (GNUNET_YES == do_shutdown) | ||
569 | return GNUNET_SYSERR; | ||
570 | if ((NULL != strstr (filename, "/.auto-share")) || | ||
571 | (NULL != strstr (filename, "\\.auto-share"))) | ||
572 | return GNUNET_OK; /* skip internal file */ | ||
573 | GNUNET_CRYPTO_hash (filename, strlen (filename), &key); | ||
574 | wi = GNUNET_CONTAINER_multihashmap_get (work_finished, &key); | ||
575 | memset (&id, 0, sizeof(struct GNUNET_HashCode)); | ||
576 | determine_id (&id, filename); | ||
577 | if (NULL != wi) | ||
578 | { | ||
579 | if (0 == memcmp (&id, &wi->id, sizeof(struct GNUNET_HashCode))) | ||
580 | return GNUNET_OK; /* skip: we did this one already */ | ||
581 | /* contents changed, need to re-do the directory... */ | ||
582 | GNUNET_assert ( | ||
583 | GNUNET_YES == | ||
584 | GNUNET_CONTAINER_multihashmap_remove (work_finished, &key, wi)); | ||
585 | } | ||
586 | else | ||
587 | { | ||
588 | wi = GNUNET_new (struct WorkItem); | ||
589 | wi->filename = GNUNET_strdup (filename); | ||
590 | } | ||
591 | wi->id = id; | ||
592 | GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi); | ||
593 | if (GNUNET_YES == do_shutdown) | ||
594 | return GNUNET_SYSERR; | ||
595 | return GNUNET_OK; | ||
596 | } | ||
597 | |||
598 | |||
599 | /** | ||
600 | * Periodically run task to update our view of the directory to share. | ||
601 | * | ||
602 | * @param cls NULL | ||
603 | */ | ||
604 | static void | ||
605 | scan (void *cls) | ||
606 | { | ||
607 | run_task = NULL; | ||
608 | start_time = GNUNET_TIME_absolute_get (); | ||
609 | (void) GNUNET_DISK_directory_scan (dir_name, &add_file, NULL); | ||
610 | schedule_next_task (); | ||
611 | } | ||
612 | |||
613 | |||
614 | /** | ||
615 | * Decide what the next task is (working or scanning) and schedule it. | ||
616 | */ | ||
617 | static void | ||
618 | schedule_next_task () | ||
619 | { | ||
620 | struct GNUNET_TIME_Relative delay; | ||
621 | |||
622 | if (GNUNET_YES == do_shutdown) | ||
623 | return; | ||
624 | GNUNET_assert (NULL == run_task); | ||
625 | if (NULL == work_head) | ||
626 | { | ||
627 | /* delay by at most 4h, at least 1s, and otherwise in between depending | ||
628 | on how long it took to scan */ | ||
629 | delay = GNUNET_TIME_absolute_get_duration (start_time); | ||
630 | delay = GNUNET_TIME_relative_saturating_multiply (delay, 100); | ||
631 | delay = GNUNET_TIME_relative_min (delay, MAX_DELAY); | ||
632 | delay = GNUNET_TIME_relative_max (delay, MIN_DELAY); | ||
633 | run_task = GNUNET_SCHEDULER_add_delayed (delay, &scan, NULL); | ||
634 | } | ||
635 | else | ||
636 | { | ||
637 | run_task = GNUNET_SCHEDULER_add_now (&work, NULL); | ||
638 | } | ||
639 | } | ||
640 | |||
641 | |||
642 | /** | ||
643 | * Main function that will be run by the scheduler. | ||
644 | * | ||
645 | * @param cls closure | ||
646 | * @param args remaining command-line arguments | ||
647 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
648 | * @param c configuration | ||
649 | */ | ||
650 | static void | ||
651 | run (void *cls, | ||
652 | char *const *args, | ||
653 | const char *cfgfile, | ||
654 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
655 | { | ||
656 | /* check arguments */ | ||
657 | if ((NULL == args[0]) || (NULL != args[1]) || | ||
658 | (GNUNET_YES != GNUNET_DISK_directory_test (args[0], GNUNET_YES))) | ||
659 | { | ||
660 | printf (_ ( | ||
661 | "You must specify one and only one directory name for automatic publication.\n")); | ||
662 | ret = -1; | ||
663 | return; | ||
664 | } | ||
665 | cfg_filename = GNUNET_strdup (cfgfile); | ||
666 | cfg = c; | ||
667 | dir_name = args[0]; | ||
668 | work_finished = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO); | ||
669 | load_state (); | ||
670 | run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
671 | &scan, | ||
672 | NULL); | ||
673 | GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL); | ||
674 | } | ||
675 | |||
676 | |||
677 | /** | ||
678 | * Free memory associated with the work item from the work_finished map. | ||
679 | * | ||
680 | * @param cls NULL (unused) | ||
681 | * @param key key of the item in the map (unused) | ||
682 | * @param value the `struct WorkItem` to free | ||
683 | * @return #GNUNET_OK to continue to iterate | ||
684 | */ | ||
685 | static int | ||
686 | free_item (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
687 | { | ||
688 | struct WorkItem *wi = value; | ||
689 | |||
690 | GNUNET_free (wi->filename); | ||
691 | GNUNET_free (wi); | ||
692 | return GNUNET_OK; | ||
693 | } | ||
694 | |||
695 | |||
696 | /** | ||
697 | * The main function to automatically publish content to GNUnet. | ||
698 | * | ||
699 | * @param argc number of arguments from the command line | ||
700 | * @param argv command line arguments | ||
701 | * @return 0 ok, 1 on error | ||
702 | */ | ||
703 | int | ||
704 | main (int argc, char *const *argv) | ||
705 | { | ||
706 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
707 | GNUNET_GETOPT_option_uint ('a', | ||
708 | "anonymity", | ||
709 | "LEVEL", | ||
710 | gettext_noop ( | ||
711 | "set the desired LEVEL of sender-anonymity"), | ||
712 | &anonymity_level), | ||
713 | |||
714 | GNUNET_GETOPT_option_flag ( | ||
715 | 'd', | ||
716 | "disable-creation-time", | ||
717 | gettext_noop ( | ||
718 | "disable adding the creation time to the metadata of the uploaded file"), | ||
719 | &do_disable_creation_time), | ||
720 | |||
721 | GNUNET_GETOPT_option_flag ( | ||
722 | 'D', | ||
723 | "disable-extractor", | ||
724 | gettext_noop ("do not use libextractor to add keywords or metadata"), | ||
725 | &disable_extractor), | ||
726 | |||
727 | GNUNET_GETOPT_option_uint ('p', | ||
728 | "priority", | ||
729 | "PRIORITY", | ||
730 | gettext_noop ( | ||
731 | "specify the priority of the content"), | ||
732 | &content_priority), | ||
733 | |||
734 | GNUNET_GETOPT_option_uint ('r', | ||
735 | "replication", | ||
736 | "LEVEL", | ||
737 | gettext_noop ( | ||
738 | "set the desired replication LEVEL"), | ||
739 | &replication_level), | ||
740 | |||
741 | GNUNET_GETOPT_option_verbose (&verbose), | ||
742 | |||
743 | GNUNET_GETOPT_OPTION_END | ||
744 | }; | ||
745 | struct WorkItem *wi; | ||
746 | int ok; | ||
747 | struct GNUNET_SIGNAL_Context *shc_chld; | ||
748 | |||
749 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
750 | return 2; | ||
751 | sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); | ||
752 | GNUNET_assert (NULL != sigpipe); | ||
753 | shc_chld = | ||
754 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | ||
755 | ok = | ||
756 | (GNUNET_OK == | ||
757 | GNUNET_PROGRAM_run ( | ||
758 | argc, | ||
759 | argv, | ||
760 | "gnunet-auto-share [OPTIONS] FILENAME", | ||
761 | gettext_noop ("Automatically publish files from a directory on GNUnet"), | ||
762 | options, | ||
763 | &run, | ||
764 | NULL)) | ||
765 | ? ret | ||
766 | : 1; | ||
767 | if (NULL != work_finished) | ||
768 | { | ||
769 | (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished, | ||
770 | &free_item, | ||
771 | NULL); | ||
772 | GNUNET_CONTAINER_multihashmap_destroy (work_finished); | ||
773 | } | ||
774 | while (NULL != (wi = work_head)) | ||
775 | { | ||
776 | GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi); | ||
777 | GNUNET_free (wi->filename); | ||
778 | GNUNET_free (wi); | ||
779 | } | ||
780 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
781 | shc_chld = NULL; | ||
782 | GNUNET_DISK_pipe_close (sigpipe); | ||
783 | sigpipe = NULL; | ||
784 | GNUNET_free (cfg_filename); | ||
785 | cfg_filename = NULL; | ||
786 | GNUNET_free_nz ((void *) argv); | ||
787 | return ok; | ||
788 | } | ||
789 | |||
790 | |||
791 | /* end of gnunet-auto-share.c */ | ||
diff --git a/src/cli/fs/gnunet-directory.c b/src/cli/fs/gnunet-directory.c new file mode 100644 index 000000000..ab9f2905a --- /dev/null +++ b/src/cli/fs/gnunet-directory.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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 fs/gnunet-directory.c | ||
22 | * @brief display content of GNUnet directories | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | |||
27 | #include "gnunet_fs_service.h" | ||
28 | |||
29 | static int ret; | ||
30 | |||
31 | /** | ||
32 | * Print a meta data entry. | ||
33 | * | ||
34 | * @param cls closure (unused) | ||
35 | * @param plugin_name name of the plugin that generated the meta data | ||
36 | * @param type type of the keyword | ||
37 | * @param format format of data | ||
38 | * @param data_mime_type mime type of data | ||
39 | * @param data value of the meta data | ||
40 | * @param data_size number of bytes in @a data | ||
41 | * @return always 0 (to continue iterating) | ||
42 | */ | ||
43 | static int | ||
44 | item_printer (void *cls, | ||
45 | const char *plugin_name, | ||
46 | enum EXTRACTOR_MetaType type, | ||
47 | enum EXTRACTOR_MetaFormat format, | ||
48 | const char *data_mime_type, | ||
49 | const char *data, | ||
50 | size_t data_size) | ||
51 | { | ||
52 | if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA) | ||
53 | { | ||
54 | printf (_ ("\t<original file embedded in %u bytes of meta data>\n"), | ||
55 | (unsigned int) data_size); | ||
56 | return 0; | ||
57 | } | ||
58 | if ((format != EXTRACTOR_METAFORMAT_UTF8) && | ||
59 | (format != EXTRACTOR_METAFORMAT_C_STRING)) | ||
60 | return 0; | ||
61 | if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) | ||
62 | return 0; | ||
63 | #if HAVE_LIBEXTRACTOR | ||
64 | printf ("\t%20s: %s\n", | ||
65 | dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, | ||
66 | EXTRACTOR_metatype_to_string (type)), | ||
67 | data); | ||
68 | #else | ||
69 | printf ("\t%20d: %s\n", type, data); | ||
70 | #endif | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Print an entry in a directory. | ||
77 | * | ||
78 | * @param cls closure (not used) | ||
79 | * @param filename name of the file in the directory | ||
80 | * @param uri URI of the file | ||
81 | * @param meta metadata for the file; metadata for | ||
82 | * the directory if everything else is NULL/zero | ||
83 | * @param length length of the available data for the file | ||
84 | * (of type size_t since data must certainly fit | ||
85 | * into memory; if files are larger than size_t | ||
86 | * permits, then they will certainly not be | ||
87 | * embedded with the directory itself). | ||
88 | * @param data data available for the file (length bytes) | ||
89 | */ | ||
90 | static void | ||
91 | print_entry (void *cls, | ||
92 | const char *filename, | ||
93 | const struct GNUNET_FS_Uri *uri, | ||
94 | const struct GNUNET_FS_MetaData *meta, | ||
95 | size_t length, | ||
96 | const void *data) | ||
97 | { | ||
98 | char *string; | ||
99 | char *name; | ||
100 | |||
101 | name = GNUNET_FS_meta_data_get_by_type ( | ||
102 | meta, | ||
103 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
104 | if (uri == NULL) | ||
105 | { | ||
106 | printf (_ ("Directory `%s' meta data:\n"), name ? name : ""); | ||
107 | GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL); | ||
108 | printf ("\n"); | ||
109 | printf (_ ("Directory `%s' contents:\n"), name ? name : ""); | ||
110 | GNUNET_free (name); | ||
111 | return; | ||
112 | } | ||
113 | string = GNUNET_FS_uri_to_string (uri); | ||
114 | printf ("%s (%s):\n", name ? name : "", string); | ||
115 | GNUNET_free (string); | ||
116 | GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL); | ||
117 | printf ("\n"); | ||
118 | GNUNET_free (name); | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Main function that will be run by the scheduler. | ||
124 | * | ||
125 | * @param cls closure | ||
126 | * @param args remaining command-line arguments | ||
127 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
128 | * @param cfg configuration | ||
129 | */ | ||
130 | static void | ||
131 | run (void *cls, | ||
132 | char *const *args, | ||
133 | const char *cfgfile, | ||
134 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
135 | { | ||
136 | struct GNUNET_DISK_MapHandle *map; | ||
137 | struct GNUNET_DISK_FileHandle *h; | ||
138 | void *data; | ||
139 | size_t len; | ||
140 | uint64_t size; | ||
141 | const char *filename; | ||
142 | int i; | ||
143 | |||
144 | if (NULL == args[0]) | ||
145 | { | ||
146 | fprintf (stderr, "%s", _ ("You must specify a filename to inspect.\n")); | ||
147 | ret = 1; | ||
148 | return; | ||
149 | } | ||
150 | i = 0; | ||
151 | while (NULL != (filename = args[i++])) | ||
152 | { | ||
153 | if ((GNUNET_OK != | ||
154 | GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES)) || | ||
155 | (NULL == (h = GNUNET_DISK_file_open (filename, | ||
156 | GNUNET_DISK_OPEN_READ, | ||
157 | GNUNET_DISK_PERM_NONE)))) | ||
158 | { | ||
159 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
160 | _ ("Failed to read directory `%s'\n"), | ||
161 | filename); | ||
162 | ret = 1; | ||
163 | continue; | ||
164 | } | ||
165 | len = (size_t) size; | ||
166 | data = GNUNET_DISK_file_map (h, &map, GNUNET_DISK_MAP_TYPE_READ, len); | ||
167 | GNUNET_assert (NULL != data); | ||
168 | if (GNUNET_OK != | ||
169 | GNUNET_FS_directory_list_contents (len, data, 0, &print_entry, NULL)) | ||
170 | fprintf (stdout, _ ("`%s' is not a GNUnet directory\n"), filename); | ||
171 | else | ||
172 | printf ("\n"); | ||
173 | GNUNET_DISK_file_unmap (map); | ||
174 | GNUNET_DISK_file_close (h); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * The main function to inspect GNUnet directories. | ||
181 | * | ||
182 | * @param argc number of arguments from the command line | ||
183 | * @param argv command line arguments | ||
184 | * @return 0 ok, 1 on error | ||
185 | */ | ||
186 | int | ||
187 | main (int argc, char *const *argv) | ||
188 | { | ||
189 | static struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
190 | GNUNET_GETOPT_OPTION_END | ||
191 | }; | ||
192 | |||
193 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
194 | return 2; | ||
195 | |||
196 | ret = (GNUNET_OK == | ||
197 | GNUNET_PROGRAM_run (argc, | ||
198 | argv, | ||
199 | "gnunet-directory [OPTIONS] FILENAME", | ||
200 | gettext_noop ( | ||
201 | "Display contents of a GNUnet directory"), | ||
202 | options, | ||
203 | &run, | ||
204 | NULL)) | ||
205 | ? ret | ||
206 | : 1; | ||
207 | GNUNET_free_nz ((void *) argv); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | |||
212 | /* end of gnunet-directory.c */ | ||
diff --git a/src/cli/fs/gnunet-download.c b/src/cli/fs/gnunet-download.c new file mode 100644 index 000000000..4694077e9 --- /dev/null +++ b/src/cli/fs/gnunet-download.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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 fs/gnunet-download.c | ||
22 | * @brief downloading for files on GNUnet | ||
23 | * @author Christian Grothoff | ||
24 | * @author Krista Bennett | ||
25 | * @author James Blackwell | ||
26 | * @author Igor Wronsky | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static int delete_incomplete; | ||
37 | |||
38 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
39 | |||
40 | static struct GNUNET_FS_Handle *ctx; | ||
41 | |||
42 | static struct GNUNET_FS_DownloadContext *dc; | ||
43 | |||
44 | static unsigned int anonymity = 1; | ||
45 | |||
46 | static unsigned int parallelism = 16; | ||
47 | |||
48 | static unsigned int request_parallelism = 4092; | ||
49 | |||
50 | static int do_recursive; | ||
51 | |||
52 | static char *filename; | ||
53 | |||
54 | static int local_only; | ||
55 | |||
56 | |||
57 | static void | ||
58 | cleanup_task (void *cls) | ||
59 | { | ||
60 | GNUNET_FS_stop (ctx); | ||
61 | ctx = NULL; | ||
62 | } | ||
63 | |||
64 | |||
65 | static void | ||
66 | shutdown_task (void *cls) | ||
67 | { | ||
68 | if (NULL != dc) | ||
69 | { | ||
70 | GNUNET_FS_download_stop (dc, delete_incomplete); | ||
71 | dc = NULL; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Display progress bar (if tty). | ||
78 | * | ||
79 | * @param x current position in the download | ||
80 | * @param n total size of the download | ||
81 | * @param w desired number of steps in the progress bar | ||
82 | */ | ||
83 | static void | ||
84 | display_bar (unsigned long long x, unsigned long long n, unsigned int w) | ||
85 | { | ||
86 | char buf[w + 20]; | ||
87 | unsigned int p; | ||
88 | unsigned int endeq; | ||
89 | float ratio_complete; | ||
90 | |||
91 | if (0 == isatty (1)) | ||
92 | return; | ||
93 | ratio_complete = x / (float) n; | ||
94 | endeq = ratio_complete * w; | ||
95 | GNUNET_snprintf (buf, sizeof(buf), "%3d%% [", (int) (ratio_complete * 100)); | ||
96 | for (p = 0; p < endeq; p++) | ||
97 | strcat (buf, "="); | ||
98 | for (p = endeq; p < w; p++) | ||
99 | strcat (buf, " "); | ||
100 | strcat (buf, "]\r"); | ||
101 | printf ("%s", buf); | ||
102 | fflush (stdout); | ||
103 | } | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Called by FS client to give information about the progress of an | ||
108 | * operation. | ||
109 | * | ||
110 | * @param cls closure | ||
111 | * @param info details about the event, specifying the event type | ||
112 | * and various bits about the event | ||
113 | * @return client-context (for the next progress call | ||
114 | * for this operation; should be set to NULL for | ||
115 | * SUSPEND and STOPPED events). The value returned | ||
116 | * will be passed to future callbacks in the respective | ||
117 | * field in the `struct GNUNET_FS_ProgressInfo` | ||
118 | */ | ||
119 | static void * | ||
120 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
121 | { | ||
122 | char *s; | ||
123 | const char *s2; | ||
124 | char *t; | ||
125 | |||
126 | switch (info->status) | ||
127 | { | ||
128 | case GNUNET_FS_STATUS_DOWNLOAD_START: | ||
129 | if (verbose > 1) | ||
130 | fprintf (stderr, | ||
131 | _ ("Starting download `%s'.\n"), | ||
132 | info->value.download.filename); | ||
133 | break; | ||
134 | |||
135 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
136 | if (verbose) | ||
137 | { | ||
138 | s = GNUNET_strdup ( | ||
139 | GNUNET_STRINGS_relative_time_to_string (info->value.download.eta, | ||
140 | GNUNET_YES)); | ||
141 | if (info->value.download.specifics.progress.block_download_duration | ||
142 | .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) | ||
143 | s2 = _ ("<unknown time>"); | ||
144 | else | ||
145 | s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download | ||
146 | .specifics.progress | ||
147 | .block_download_duration, | ||
148 | GNUNET_YES); | ||
149 | t = GNUNET_STRINGS_byte_size_fancy ( | ||
150 | info->value.download.completed * 1000LL | ||
151 | / (info->value.download.duration.rel_value_us + 1)); | ||
152 | fprintf ( | ||
153 | stdout, | ||
154 | _ ( | ||
155 | "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"), | ||
156 | info->value.download.filename, | ||
157 | (unsigned long long) info->value.download.completed, | ||
158 | (unsigned long long) info->value.download.size, | ||
159 | s, | ||
160 | t, | ||
161 | s2); | ||
162 | GNUNET_free (s); | ||
163 | GNUNET_free (t); | ||
164 | } | ||
165 | else | ||
166 | { | ||
167 | display_bar (info->value.download.completed, | ||
168 | info->value.download.size, | ||
169 | 60); | ||
170 | } | ||
171 | break; | ||
172 | |||
173 | case GNUNET_FS_STATUS_DOWNLOAD_ERROR: | ||
174 | if (0 != isatty (1)) | ||
175 | fprintf (stdout, "\n"); | ||
176 | fprintf (stderr, | ||
177 | _ ("Error downloading: %s.\n"), | ||
178 | info->value.download.specifics.error.message); | ||
179 | GNUNET_SCHEDULER_shutdown (); | ||
180 | break; | ||
181 | |||
182 | case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: | ||
183 | s = GNUNET_STRINGS_byte_size_fancy ( | ||
184 | info->value.download.completed * 1000 | ||
185 | / (info->value.download.duration.rel_value_us + 1)); | ||
186 | if (0 != isatty (1)) | ||
187 | fprintf (stdout, "\n"); | ||
188 | fprintf (stdout, | ||
189 | _ ("Downloading `%s' done (%s/s).\n"), | ||
190 | info->value.download.filename, | ||
191 | s); | ||
192 | GNUNET_free (s); | ||
193 | if (info->value.download.dc == dc) | ||
194 | GNUNET_SCHEDULER_shutdown (); | ||
195 | break; | ||
196 | |||
197 | case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: | ||
198 | if (info->value.download.dc == dc) | ||
199 | GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); | ||
200 | break; | ||
201 | |||
202 | case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: | ||
203 | case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: | ||
204 | break; | ||
205 | |||
206 | default: | ||
207 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
208 | break; | ||
209 | } | ||
210 | return NULL; | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Main function that will be run by the scheduler. | ||
216 | * | ||
217 | * @param cls closure | ||
218 | * @param args remaining command-line arguments | ||
219 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
220 | * @param c configuration | ||
221 | */ | ||
222 | static void | ||
223 | run (void *cls, | ||
224 | char *const *args, | ||
225 | const char *cfgfile, | ||
226 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
227 | { | ||
228 | struct GNUNET_FS_Uri *uri; | ||
229 | char *emsg; | ||
230 | enum GNUNET_FS_DownloadOptions options; | ||
231 | |||
232 | if (NULL == args[0]) | ||
233 | { | ||
234 | fprintf (stderr, "%s", _ ("You need to specify a URI argument.\n")); | ||
235 | return; | ||
236 | } | ||
237 | uri = GNUNET_FS_uri_parse (args[0], &emsg); | ||
238 | if (NULL == uri) | ||
239 | { | ||
240 | fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg); | ||
241 | GNUNET_free (emsg); | ||
242 | ret = 1; | ||
243 | return; | ||
244 | } | ||
245 | if ((! GNUNET_FS_uri_test_chk (uri)) && (! GNUNET_FS_uri_test_loc (uri))) | ||
246 | { | ||
247 | fprintf (stderr, "%s", _ ("Only CHK or LOC URIs supported.\n")); | ||
248 | ret = 1; | ||
249 | GNUNET_FS_uri_destroy (uri); | ||
250 | return; | ||
251 | } | ||
252 | if (NULL == filename) | ||
253 | { | ||
254 | fprintf (stderr, "%s", _ ("Target filename must be specified.\n")); | ||
255 | ret = 1; | ||
256 | GNUNET_FS_uri_destroy (uri); | ||
257 | return; | ||
258 | } | ||
259 | cfg = c; | ||
260 | ctx = GNUNET_FS_start (cfg, | ||
261 | "gnunet-download", | ||
262 | &progress_cb, | ||
263 | NULL, | ||
264 | GNUNET_FS_FLAGS_NONE, | ||
265 | GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, | ||
266 | parallelism, | ||
267 | GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, | ||
268 | request_parallelism, | ||
269 | GNUNET_FS_OPTIONS_END); | ||
270 | if (NULL == ctx) | ||
271 | { | ||
272 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
273 | GNUNET_FS_uri_destroy (uri); | ||
274 | ret = 1; | ||
275 | return; | ||
276 | } | ||
277 | options = GNUNET_FS_DOWNLOAD_OPTION_NONE; | ||
278 | if (do_recursive) | ||
279 | options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE; | ||
280 | if (local_only) | ||
281 | options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY; | ||
282 | dc = GNUNET_FS_download_start (ctx, | ||
283 | uri, | ||
284 | NULL, | ||
285 | filename, | ||
286 | NULL, | ||
287 | 0, | ||
288 | GNUNET_FS_uri_chk_get_file_size (uri), | ||
289 | anonymity, | ||
290 | options, | ||
291 | NULL, | ||
292 | NULL); | ||
293 | GNUNET_FS_uri_destroy (uri); | ||
294 | if (dc == NULL) | ||
295 | { | ||
296 | GNUNET_FS_stop (ctx); | ||
297 | ctx = NULL; | ||
298 | return; | ||
299 | } | ||
300 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
301 | } | ||
302 | |||
303 | |||
304 | /** | ||
305 | * The main function to download GNUnet. | ||
306 | * | ||
307 | * @param argc number of arguments from the command line | ||
308 | * @param argv command line arguments | ||
309 | * @return 0 ok, 1 on error | ||
310 | */ | ||
311 | int | ||
312 | main (int argc, char *const *argv) | ||
313 | { | ||
314 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
315 | { GNUNET_GETOPT_option_uint ('a', | ||
316 | "anonymity", | ||
317 | "LEVEL", | ||
318 | gettext_noop ( | ||
319 | "set the desired LEVEL of receiver-anonymity"), | ||
320 | &anonymity), | ||
321 | |||
322 | GNUNET_GETOPT_option_flag ( | ||
323 | 'D', | ||
324 | "delete-incomplete", | ||
325 | gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"), | ||
326 | &delete_incomplete), | ||
327 | |||
328 | GNUNET_GETOPT_option_flag ( | ||
329 | 'n', | ||
330 | "no-network", | ||
331 | gettext_noop ("only search the local peer (no P2P network search)"), | ||
332 | &local_only), | ||
333 | GNUNET_GETOPT_option_string ('o', | ||
334 | "output", | ||
335 | "FILENAME", | ||
336 | gettext_noop ("write the file to FILENAME"), | ||
337 | &filename), | ||
338 | GNUNET_GETOPT_option_uint ( | ||
339 | 'p', | ||
340 | "parallelism", | ||
341 | "DOWNLOADS", | ||
342 | gettext_noop ( | ||
343 | "set the maximum number of parallel downloads that is allowed"), | ||
344 | ¶llelism), | ||
345 | GNUNET_GETOPT_option_uint ( | ||
346 | 'r', | ||
347 | "request-parallelism", | ||
348 | "REQUESTS", | ||
349 | gettext_noop ( | ||
350 | "set the maximum number of parallel requests for blocks that is allowed"), | ||
351 | &request_parallelism), | ||
352 | GNUNET_GETOPT_option_flag ('R', | ||
353 | "recursive", | ||
354 | gettext_noop ( | ||
355 | "download a GNUnet directory recursively"), | ||
356 | &do_recursive), | ||
357 | GNUNET_GETOPT_option_increment_uint ( | ||
358 | 'V', | ||
359 | "verbose", | ||
360 | gettext_noop ("be verbose (print progress information)"), | ||
361 | &verbose), | ||
362 | GNUNET_GETOPT_OPTION_END }; | ||
363 | |||
364 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
365 | return 2; | ||
366 | |||
367 | ret = | ||
368 | (GNUNET_OK == | ||
369 | GNUNET_PROGRAM_run ( | ||
370 | argc, | ||
371 | argv, | ||
372 | "gnunet-download [OPTIONS] URI", | ||
373 | gettext_noop ( | ||
374 | "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"), | ||
375 | options, | ||
376 | &run, | ||
377 | NULL)) | ||
378 | ? ret | ||
379 | : 1; | ||
380 | GNUNET_free_nz ((void *) argv); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | |||
385 | /* end of gnunet-download.c */ | ||
diff --git a/src/cli/fs/gnunet-fs.c b/src/cli/fs/gnunet-fs.c new file mode 100644 index 000000000..21e3c4a40 --- /dev/null +++ b/src/cli/fs/gnunet-fs.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011 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 fs/gnunet-fs.c | ||
22 | * @brief special file-sharing functions | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | |||
27 | #include "gnunet_fs_service.h" | ||
28 | |||
29 | /** | ||
30 | * Return value. | ||
31 | */ | ||
32 | static int ret; | ||
33 | |||
34 | /** | ||
35 | * Handle to FS service. | ||
36 | */ | ||
37 | static struct GNUNET_FS_Handle *fs; | ||
38 | |||
39 | /** | ||
40 | * Handle for the index listing operation. | ||
41 | */ | ||
42 | static struct GNUNET_FS_GetIndexedContext *gic; | ||
43 | |||
44 | /** | ||
45 | * Option -i given? | ||
46 | */ | ||
47 | static int list_indexed_files; | ||
48 | |||
49 | /** | ||
50 | * Option -v given? | ||
51 | */ | ||
52 | static unsigned int verbose; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Print indexed filenames to stdout. | ||
57 | * | ||
58 | * @param cls closure | ||
59 | * @param filename the name of the file | ||
60 | * @param file_id hash of the contents of the indexed file | ||
61 | * @return #GNUNET_OK to continue iteration | ||
62 | */ | ||
63 | static enum GNUNET_GenericReturnValue | ||
64 | print_indexed (void *cls, | ||
65 | const char *filename, | ||
66 | const struct GNUNET_HashCode *file_id) | ||
67 | { | ||
68 | if (NULL == filename) | ||
69 | { | ||
70 | gic = NULL; | ||
71 | GNUNET_SCHEDULER_shutdown (); | ||
72 | return GNUNET_OK; | ||
73 | } | ||
74 | if (verbose) | ||
75 | fprintf (stdout, | ||
76 | "%s: %s\n", | ||
77 | GNUNET_h2s (file_id), | ||
78 | filename); | ||
79 | else | ||
80 | fprintf (stdout, | ||
81 | "%s\n", | ||
82 | filename); | ||
83 | return GNUNET_OK; | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Function run on shutdown. | ||
89 | * | ||
90 | * @param cls NULL | ||
91 | */ | ||
92 | static void | ||
93 | do_shutdown (void *cls) | ||
94 | { | ||
95 | (void) cls; | ||
96 | if (NULL != gic) | ||
97 | { | ||
98 | GNUNET_FS_get_indexed_files_cancel (gic); | ||
99 | gic = NULL; | ||
100 | } | ||
101 | if (NULL != fs) | ||
102 | { | ||
103 | GNUNET_FS_stop (fs); | ||
104 | fs = NULL; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Main function that will be run by the scheduler. | ||
111 | * | ||
112 | * @param cls closure | ||
113 | * @param args remaining command-line arguments | ||
114 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
115 | * @param cfg configuration | ||
116 | */ | ||
117 | static void | ||
118 | run (void *cls, | ||
119 | char *const *args, | ||
120 | const char *cfgfile, | ||
121 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
122 | { | ||
123 | if (! list_indexed_files) | ||
124 | return; | ||
125 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
126 | NULL); | ||
127 | fs = GNUNET_FS_start (cfg, | ||
128 | "gnunet-fs", | ||
129 | NULL, | ||
130 | NULL, | ||
131 | GNUNET_FS_FLAGS_NONE, | ||
132 | GNUNET_FS_OPTIONS_END); | ||
133 | if (NULL == fs) | ||
134 | { | ||
135 | ret = 1; | ||
136 | return; | ||
137 | } | ||
138 | gic = GNUNET_FS_get_indexed_files (fs, | ||
139 | &print_indexed, | ||
140 | NULL); | ||
141 | if (NULL == gic) | ||
142 | { | ||
143 | ret = 2; | ||
144 | GNUNET_SCHEDULER_shutdown (); | ||
145 | return; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | |||
150 | /** | ||
151 | * The main function to access special file-sharing functions. | ||
152 | * | ||
153 | * @param argc number of arguments from the command line | ||
154 | * @param argv command line arguments | ||
155 | * @return 0 ok, 1 on error | ||
156 | */ | ||
157 | int | ||
158 | main (int argc, | ||
159 | char *const *argv) | ||
160 | { | ||
161 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
162 | GNUNET_GETOPT_option_flag ('i', | ||
163 | "list-indexed", | ||
164 | gettext_noop ( | ||
165 | "print a list of all indexed files"), | ||
166 | &list_indexed_files), | ||
167 | |||
168 | GNUNET_GETOPT_option_verbose (&verbose), | ||
169 | GNUNET_GETOPT_OPTION_END | ||
170 | }; | ||
171 | |||
172 | if (GNUNET_OK != | ||
173 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
174 | &argc, &argv)) | ||
175 | return 2; | ||
176 | ret = (GNUNET_OK == | ||
177 | GNUNET_PROGRAM_run (argc, | ||
178 | argv, | ||
179 | "gnunet-fs [OPTIONS]", | ||
180 | gettext_noop ("Special file-sharing operations"), | ||
181 | options, | ||
182 | &run, | ||
183 | NULL)) | ||
184 | ? ret | ||
185 | : 1; | ||
186 | GNUNET_free_nz ((void *) argv); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | |||
191 | /* end of gnunet-fs.c */ | ||
diff --git a/src/cli/fs/gnunet-publish.c b/src/cli/fs/gnunet-publish.c new file mode 100644 index 000000000..7a87130de --- /dev/null +++ b/src/cli/fs/gnunet-publish.c | |||
@@ -0,0 +1,1009 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001-2013 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 fs/gnunet-publish.c | ||
22 | * @brief publishing files on GNUnet | ||
23 | * @author Christian Grothoff | ||
24 | * @author Krista Bennett | ||
25 | * @author James Blackwell | ||
26 | * @author Igor Wronsky | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_identity_service.h" | ||
32 | |||
33 | /** | ||
34 | * Global return value from #main(). | ||
35 | */ | ||
36 | static int ret; | ||
37 | |||
38 | /** | ||
39 | * Command line option 'verbose' set | ||
40 | */ | ||
41 | static unsigned int verbose; | ||
42 | |||
43 | /** | ||
44 | * Handle to our configuration. | ||
45 | */ | ||
46 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
47 | |||
48 | /** | ||
49 | * Handle for interaction with file-sharing service. | ||
50 | */ | ||
51 | static struct GNUNET_FS_Handle *ctx; | ||
52 | |||
53 | /** | ||
54 | * Handle to FS-publishing operation. | ||
55 | */ | ||
56 | static struct GNUNET_FS_PublishContext *pc; | ||
57 | |||
58 | /** | ||
59 | * Meta-data provided via command-line option. | ||
60 | */ | ||
61 | static struct GNUNET_FS_MetaData *meta; | ||
62 | |||
63 | /** | ||
64 | * Keywords provided via command-line option. | ||
65 | */ | ||
66 | static struct GNUNET_FS_Uri *topKeywords; | ||
67 | |||
68 | /** | ||
69 | * Options we set for published blocks. | ||
70 | */ | ||
71 | static struct GNUNET_FS_BlockOptions bo = { { 0LL }, 1, 365, 1 }; | ||
72 | |||
73 | /** | ||
74 | * Value of URI provided on command-line (when not publishing | ||
75 | * a file but just creating UBlocks to refer to an existing URI). | ||
76 | */ | ||
77 | static char *uri_string; | ||
78 | |||
79 | /** | ||
80 | * Value of URI provided on command-line (when not publishing | ||
81 | * a file but just creating UBlocks to refer to an existing URI); | ||
82 | * parsed version of 'uri_string'. | ||
83 | */ | ||
84 | static struct GNUNET_FS_Uri *uri; | ||
85 | |||
86 | /** | ||
87 | * Command-line option for namespace publishing: identifier for updates | ||
88 | * to this publication. | ||
89 | */ | ||
90 | static char *next_id; | ||
91 | |||
92 | /** | ||
93 | * Command-line option for namespace publishing: identifier for this | ||
94 | * publication. | ||
95 | */ | ||
96 | static char *this_id; | ||
97 | |||
98 | /** | ||
99 | * Command-line option identifying the pseudonym to use for the publication. | ||
100 | */ | ||
101 | static char *pseudonym; | ||
102 | |||
103 | /** | ||
104 | * Command-line option for 'inserting' | ||
105 | */ | ||
106 | static int do_insert; | ||
107 | |||
108 | /** | ||
109 | * Command-line option to disable meta data extraction. | ||
110 | */ | ||
111 | static int disable_extractor; | ||
112 | |||
113 | /** | ||
114 | * Command-line option to merely simulate publishing operation. | ||
115 | */ | ||
116 | static int do_simulate; | ||
117 | |||
118 | /** | ||
119 | * Command-line option to only perform meta data extraction, but not publish. | ||
120 | */ | ||
121 | static int extract_only; | ||
122 | |||
123 | /** | ||
124 | * Command-line option to disable adding creation time. | ||
125 | */ | ||
126 | static int enable_creation_time; | ||
127 | |||
128 | /** | ||
129 | * Handle to the directory scanner (for recursive insertions). | ||
130 | */ | ||
131 | static struct GNUNET_FS_DirScanner *ds; | ||
132 | |||
133 | /** | ||
134 | * Which namespace do we publish to? NULL if we do not publish to | ||
135 | * a namespace. | ||
136 | */ | ||
137 | static struct GNUNET_IDENTITY_Ego *namespace; | ||
138 | |||
139 | /** | ||
140 | * Handle to identity service. | ||
141 | */ | ||
142 | static struct GNUNET_IDENTITY_Handle *identity; | ||
143 | |||
144 | |||
145 | /** | ||
146 | * We are finished with the publishing operation, clean up all | ||
147 | * FS state. | ||
148 | * | ||
149 | * @param cls NULL | ||
150 | */ | ||
151 | static void | ||
152 | do_stop_task (void *cls) | ||
153 | { | ||
154 | struct GNUNET_FS_PublishContext *p; | ||
155 | |||
156 | if (NULL != ds) | ||
157 | { | ||
158 | GNUNET_FS_directory_scan_abort (ds); | ||
159 | ds = NULL; | ||
160 | } | ||
161 | if (NULL != identity) | ||
162 | { | ||
163 | GNUNET_IDENTITY_disconnect (identity); | ||
164 | identity = NULL; | ||
165 | } | ||
166 | if (NULL != pc) | ||
167 | { | ||
168 | p = pc; | ||
169 | pc = NULL; | ||
170 | GNUNET_FS_publish_stop (p); | ||
171 | } | ||
172 | if (NULL != ctx) | ||
173 | { | ||
174 | GNUNET_FS_stop (ctx); | ||
175 | ctx = NULL; | ||
176 | } | ||
177 | if (NULL != meta) | ||
178 | { | ||
179 | GNUNET_FS_meta_data_destroy (meta); | ||
180 | meta = NULL; | ||
181 | } | ||
182 | if (NULL != uri) | ||
183 | { | ||
184 | GNUNET_FS_uri_destroy (uri); | ||
185 | uri = NULL; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Called by FS client to give information about the progress of an | ||
192 | * operation. | ||
193 | * | ||
194 | * @param cls closure | ||
195 | * @param info details about the event, specifying the event type | ||
196 | * and various bits about the event | ||
197 | * @return client-context (for the next progress call | ||
198 | * for this operation; should be set to NULL for | ||
199 | * SUSPEND and STOPPED events). The value returned | ||
200 | * will be passed to future callbacks in the respective | ||
201 | * field in the GNUNET_FS_ProgressInfo struct. | ||
202 | */ | ||
203 | static void * | ||
204 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
205 | { | ||
206 | const char *s; | ||
207 | char *suri; | ||
208 | |||
209 | switch (info->status) | ||
210 | { | ||
211 | case GNUNET_FS_STATUS_PUBLISH_START: | ||
212 | break; | ||
213 | |||
214 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS: | ||
215 | if (verbose) | ||
216 | { | ||
217 | s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta, | ||
218 | GNUNET_YES); | ||
219 | fprintf (stdout, | ||
220 | _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"), | ||
221 | info->value.publish.filename, | ||
222 | (unsigned long long) info->value.publish.completed, | ||
223 | (unsigned long long) info->value.publish.size, | ||
224 | s); | ||
225 | } | ||
226 | break; | ||
227 | |||
228 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY: | ||
229 | if (verbose) | ||
230 | { | ||
231 | s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.specifics | ||
232 | .progress_directory.eta, | ||
233 | GNUNET_YES); | ||
234 | fprintf (stdout, | ||
235 | _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"), | ||
236 | info->value.publish.filename, | ||
237 | (unsigned long long) | ||
238 | info->value.publish.specifics.progress_directory.completed, | ||
239 | (unsigned long long) | ||
240 | info->value.publish.specifics.progress_directory.total, | ||
241 | s); | ||
242 | } | ||
243 | break; | ||
244 | |||
245 | case GNUNET_FS_STATUS_PUBLISH_ERROR: | ||
246 | fprintf (stderr, | ||
247 | _ ("Error publishing: %s.\n"), | ||
248 | info->value.publish.specifics.error.message); | ||
249 | ret = 1; | ||
250 | GNUNET_SCHEDULER_shutdown (); | ||
251 | break; | ||
252 | |||
253 | case GNUNET_FS_STATUS_PUBLISH_COMPLETED: | ||
254 | fprintf (stdout, | ||
255 | _ ("Publishing `%s' done.\n"), | ||
256 | info->value.publish.filename); | ||
257 | suri = | ||
258 | GNUNET_FS_uri_to_string (info->value.publish.specifics.completed.chk_uri); | ||
259 | fprintf (stdout, _ ("URI is `%s'.\n"), suri); | ||
260 | GNUNET_free (suri); | ||
261 | if (NULL != info->value.publish.specifics.completed.sks_uri) | ||
262 | { | ||
263 | suri = GNUNET_FS_uri_to_string ( | ||
264 | info->value.publish.specifics.completed.sks_uri); | ||
265 | fprintf (stdout, _ ("Namespace URI is `%s'.\n"), suri); | ||
266 | GNUNET_free (suri); | ||
267 | } | ||
268 | if (NULL == info->value.publish.pctx) | ||
269 | { | ||
270 | ret = 0; | ||
271 | GNUNET_SCHEDULER_shutdown (); | ||
272 | } | ||
273 | break; | ||
274 | |||
275 | case GNUNET_FS_STATUS_PUBLISH_STOPPED: | ||
276 | GNUNET_break (NULL == pc); | ||
277 | return NULL; | ||
278 | |||
279 | case GNUNET_FS_STATUS_UNINDEX_START: | ||
280 | fprintf (stderr, "%s", _ ("Starting cleanup after abort\n")); | ||
281 | return NULL; | ||
282 | |||
283 | case GNUNET_FS_STATUS_UNINDEX_PROGRESS: | ||
284 | return NULL; | ||
285 | |||
286 | case GNUNET_FS_STATUS_UNINDEX_COMPLETED: | ||
287 | fprintf (stderr, "%s", _ ("Cleanup after abort completed.\n")); | ||
288 | GNUNET_FS_unindex_stop (info->value.unindex.uc); | ||
289 | return NULL; | ||
290 | |||
291 | case GNUNET_FS_STATUS_UNINDEX_ERROR: | ||
292 | fprintf (stderr, "%s", _ ("Cleanup after abort failed.\n")); | ||
293 | GNUNET_FS_unindex_stop (info->value.unindex.uc); | ||
294 | return NULL; | ||
295 | |||
296 | case GNUNET_FS_STATUS_UNINDEX_STOPPED: | ||
297 | return NULL; | ||
298 | |||
299 | default: | ||
300 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
301 | return NULL; | ||
302 | } | ||
303 | return ""; /* non-null */ | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * Print metadata entries (except binary | ||
309 | * metadata and the filename). | ||
310 | * | ||
311 | * @param cls closure | ||
312 | * @param plugin_name name of the plugin that generated the meta data | ||
313 | * @param type type of the meta data | ||
314 | * @param format format of data | ||
315 | * @param data_mime_type mime type of @a data | ||
316 | * @param data value of the meta data | ||
317 | * @param data_size number of bytes in @a data | ||
318 | * @return always 0 | ||
319 | */ | ||
320 | static int | ||
321 | meta_printer (void *cls, | ||
322 | const char *plugin_name, | ||
323 | enum EXTRACTOR_MetaType type, | ||
324 | enum EXTRACTOR_MetaFormat format, | ||
325 | const char *data_mime_type, | ||
326 | const char *data, | ||
327 | size_t data_size) | ||
328 | { | ||
329 | if ((EXTRACTOR_METAFORMAT_UTF8 != format) && | ||
330 | (EXTRACTOR_METAFORMAT_C_STRING != format)) | ||
331 | return 0; | ||
332 | if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) | ||
333 | return 0; | ||
334 | #if HAVE_LIBEXTRACTOR | ||
335 | fprintf (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data); | ||
336 | #else | ||
337 | fprintf (stdout, "\t%d - %s\n", type, data); | ||
338 | #endif | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | |||
343 | /** | ||
344 | * Iterator printing keywords | ||
345 | * | ||
346 | * @param cls closure | ||
347 | * @param keyword the keyword | ||
348 | * @param is_mandatory is the keyword mandatory (in a search) | ||
349 | * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to abort | ||
350 | */ | ||
351 | static int | ||
352 | keyword_printer (void *cls, const char *keyword, int is_mandatory) | ||
353 | { | ||
354 | fprintf (stdout, "\t%s\n", keyword); | ||
355 | return GNUNET_OK; | ||
356 | } | ||
357 | |||
358 | |||
359 | /** | ||
360 | * Function called on all entries before the publication. This is | ||
361 | * where we perform modifications to the default based on command-line | ||
362 | * options. | ||
363 | * | ||
364 | * @param cls closure | ||
365 | * @param fi the entry in the publish-structure | ||
366 | * @param length length of the file or directory | ||
367 | * @param m metadata for the file or directory (can be modified) | ||
368 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
369 | * @param bo block options | ||
370 | * @param do_index should we index? | ||
371 | * @param client_info pointer to client context set upon creation (can be modified) | ||
372 | * @return #GNUNET_OK to continue, #GNUNET_NO to remove | ||
373 | * this entry from the directory, #GNUNET_SYSERR | ||
374 | * to abort the iteration | ||
375 | */ | ||
376 | static int | ||
377 | publish_inspector (void *cls, | ||
378 | struct GNUNET_FS_FileInformation *fi, | ||
379 | uint64_t length, | ||
380 | struct GNUNET_FS_MetaData *m, | ||
381 | struct GNUNET_FS_Uri **uri, | ||
382 | struct GNUNET_FS_BlockOptions *bo, | ||
383 | int *do_index, | ||
384 | void **client_info) | ||
385 | { | ||
386 | char *fn; | ||
387 | char *fs; | ||
388 | struct GNUNET_FS_Uri *new_uri; | ||
389 | |||
390 | if (cls == fi) | ||
391 | return GNUNET_OK; | ||
392 | if ((disable_extractor) && (NULL != *uri)) | ||
393 | { | ||
394 | GNUNET_FS_uri_destroy (*uri); | ||
395 | *uri = NULL; | ||
396 | } | ||
397 | if (NULL != topKeywords) | ||
398 | { | ||
399 | if (NULL != *uri) | ||
400 | { | ||
401 | new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri); | ||
402 | GNUNET_FS_uri_destroy (*uri); | ||
403 | *uri = new_uri; | ||
404 | GNUNET_FS_uri_destroy (topKeywords); | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | *uri = topKeywords; | ||
409 | } | ||
410 | topKeywords = NULL; | ||
411 | } | ||
412 | if (NULL != meta) | ||
413 | { | ||
414 | GNUNET_FS_meta_data_merge (m, meta); | ||
415 | GNUNET_FS_meta_data_destroy (meta); | ||
416 | meta = NULL; | ||
417 | } | ||
418 | if (enable_creation_time) | ||
419 | GNUNET_FS_meta_data_add_publication_date (m); | ||
420 | if (extract_only) | ||
421 | { | ||
422 | fn = GNUNET_FS_meta_data_get_by_type ( | ||
423 | m, | ||
424 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
425 | fs = GNUNET_STRINGS_byte_size_fancy (length); | ||
426 | fprintf (stdout, _ ("Meta data for file `%s' (%s)\n"), fn, fs); | ||
427 | GNUNET_FS_meta_data_iterate (m, &meta_printer, NULL); | ||
428 | fprintf (stdout, _ ("Keywords for file `%s' (%s)\n"), fn, fs); | ||
429 | GNUNET_free (fn); | ||
430 | GNUNET_free (fs); | ||
431 | if (NULL != *uri) | ||
432 | GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL); | ||
433 | fprintf (stdout, "%s", "\n"); | ||
434 | } | ||
435 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m)) | ||
436 | GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi); | ||
437 | return GNUNET_OK; | ||
438 | } | ||
439 | |||
440 | |||
441 | /** | ||
442 | * Function called upon completion of the publishing | ||
443 | * of the UBLOCK for the SKS URI. As this is the last | ||
444 | * step, stop our interaction with FS (clean up). | ||
445 | * | ||
446 | * @param cls NULL (closure) | ||
447 | * @param sks_uri URI for the block that was published | ||
448 | * @param emsg error message, NULL on success | ||
449 | */ | ||
450 | static void | ||
451 | uri_sks_continuation (void *cls, | ||
452 | const struct GNUNET_FS_Uri *sks_uri, | ||
453 | const char *emsg) | ||
454 | { | ||
455 | if (NULL != emsg) | ||
456 | { | ||
457 | fprintf (stderr, "%s\n", emsg); | ||
458 | ret = 1; | ||
459 | } | ||
460 | GNUNET_SCHEDULER_shutdown (); | ||
461 | } | ||
462 | |||
463 | |||
464 | /** | ||
465 | * Function called upon completion of the publishing | ||
466 | * of the UBLOCK for the KSK URI. Continue with | ||
467 | * publishing the SKS URI (if applicable) or clean up. | ||
468 | * | ||
469 | * @param cls NULL (closure) | ||
470 | * @param ksk_uri URI for the block that was published | ||
471 | * @param emsg error message, NULL on success | ||
472 | */ | ||
473 | static void | ||
474 | uri_ksk_continuation (void *cls, | ||
475 | const struct GNUNET_FS_Uri *ksk_uri, | ||
476 | const char *emsg) | ||
477 | { | ||
478 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; | ||
479 | const struct GNUNET_CRYPTO_PrivateKey *pk; | ||
480 | |||
481 | if (NULL != emsg) | ||
482 | { | ||
483 | fprintf (stderr, "%s\n", emsg); | ||
484 | ret = 1; | ||
485 | } | ||
486 | if (NULL == namespace) | ||
487 | { | ||
488 | GNUNET_SCHEDULER_shutdown (); | ||
489 | return; | ||
490 | } | ||
491 | pk = GNUNET_IDENTITY_ego_get_private_key (namespace); | ||
492 | if (GNUNET_PUBLIC_KEY_TYPE_ECDSA != ntohl (pk->type)) | ||
493 | return; | ||
494 | priv = &pk->ecdsa_key; | ||
495 | GNUNET_FS_publish_sks (ctx, | ||
496 | priv, | ||
497 | this_id, | ||
498 | next_id, | ||
499 | meta, | ||
500 | uri, | ||
501 | &bo, | ||
502 | GNUNET_FS_PUBLISH_OPTION_NONE, | ||
503 | &uri_sks_continuation, | ||
504 | NULL); | ||
505 | } | ||
506 | |||
507 | |||
508 | /** | ||
509 | * Iterate over the results from the directory scan and extract | ||
510 | * the desired information for the publishing operation. | ||
511 | * | ||
512 | * @param item root with the data from the directory scan | ||
513 | * @return handle with the information for the publishing operation | ||
514 | */ | ||
515 | static struct GNUNET_FS_FileInformation * | ||
516 | get_file_information (struct GNUNET_FS_ShareTreeItem *item) | ||
517 | { | ||
518 | struct GNUNET_FS_FileInformation *fi; | ||
519 | struct GNUNET_FS_FileInformation *fic; | ||
520 | struct GNUNET_FS_ShareTreeItem *child; | ||
521 | |||
522 | if (GNUNET_YES == item->is_directory) | ||
523 | { | ||
524 | if (NULL == item->meta) | ||
525 | item->meta = GNUNET_FS_meta_data_create (); | ||
526 | GNUNET_FS_meta_data_delete (item->meta, | ||
527 | EXTRACTOR_METATYPE_MIMETYPE, | ||
528 | NULL, | ||
529 | 0); | ||
530 | GNUNET_FS_meta_data_make_directory (item->meta); | ||
531 | if (NULL == item->ksk_uri) | ||
532 | { | ||
533 | const char *mime = GNUNET_FS_DIRECTORY_MIME; | ||
534 | item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime); | ||
535 | } | ||
536 | else | ||
537 | GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri, | ||
538 | GNUNET_FS_DIRECTORY_MIME, | ||
539 | GNUNET_NO); | ||
540 | fi = GNUNET_FS_file_information_create_empty_directory (ctx, | ||
541 | NULL, | ||
542 | item->ksk_uri, | ||
543 | item->meta, | ||
544 | &bo, | ||
545 | item->filename); | ||
546 | for (child = item->children_head; child; child = child->next) | ||
547 | { | ||
548 | fic = get_file_information (child); | ||
549 | GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic)); | ||
550 | } | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | fi = GNUNET_FS_file_information_create_from_file (ctx, | ||
555 | NULL, | ||
556 | item->filename, | ||
557 | item->ksk_uri, | ||
558 | item->meta, | ||
559 | ! do_insert, | ||
560 | &bo); | ||
561 | } | ||
562 | return fi; | ||
563 | } | ||
564 | |||
565 | |||
566 | /** | ||
567 | * We've finished scanning the directory and optimized the meta data. | ||
568 | * Begin the publication process. | ||
569 | * | ||
570 | * @param directory_scan_result result from the directory scan, freed in this function | ||
571 | */ | ||
572 | static void | ||
573 | directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result) | ||
574 | { | ||
575 | struct GNUNET_FS_FileInformation *fi; | ||
576 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; | ||
577 | const struct GNUNET_CRYPTO_PrivateKey *pk; | ||
578 | |||
579 | fi = get_file_information (directory_scan_result); | ||
580 | GNUNET_FS_share_tree_free (directory_scan_result); | ||
581 | if (NULL == fi) | ||
582 | { | ||
583 | fprintf (stderr, "%s", _ ("Could not publish\n")); | ||
584 | ret = 1; | ||
585 | GNUNET_SCHEDULER_shutdown (); | ||
586 | return; | ||
587 | } | ||
588 | GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL); | ||
589 | if (extract_only) | ||
590 | { | ||
591 | GNUNET_FS_file_information_destroy (fi, NULL, NULL); | ||
592 | GNUNET_SCHEDULER_shutdown (); | ||
593 | return; | ||
594 | } | ||
595 | priv = NULL; | ||
596 | if (NULL != namespace) | ||
597 | { | ||
598 | pk = GNUNET_IDENTITY_ego_get_private_key (namespace); | ||
599 | GNUNET_assert (GNUNET_PUBLIC_KEY_TYPE_ECDSA == ntohl (pk->type)); | ||
600 | priv = &pk->ecdsa_key; | ||
601 | } | ||
602 | pc = GNUNET_FS_publish_start (ctx, | ||
603 | fi, | ||
604 | priv, | ||
605 | this_id, | ||
606 | next_id, | ||
607 | (do_simulate) | ||
608 | ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY | ||
609 | : GNUNET_FS_PUBLISH_OPTION_NONE); | ||
610 | if (NULL == pc) | ||
611 | { | ||
612 | fprintf (stderr, "%s", _ ("Could not start publishing.\n")); | ||
613 | ret = 1; | ||
614 | GNUNET_SCHEDULER_shutdown (); | ||
615 | return; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | |||
620 | /** | ||
621 | * Function called by the directory scanner as we build the tree | ||
622 | * that we will need to publish later. | ||
623 | * | ||
624 | * @param cls closure | ||
625 | * @param filename which file we are making progress on | ||
626 | * @param is_directory #GNUNET_YES if this is a directory, | ||
627 | * #GNUNET_NO if this is a file | ||
628 | * #GNUNET_SYSERR if it is neither (or unknown) | ||
629 | * @param reason kind of progress we are making | ||
630 | */ | ||
631 | static void | ||
632 | directory_scan_cb (void *cls, | ||
633 | const char *filename, | ||
634 | int is_directory, | ||
635 | enum GNUNET_FS_DirScannerProgressUpdateReason reason) | ||
636 | { | ||
637 | struct GNUNET_FS_ShareTreeItem *directory_scan_result; | ||
638 | |||
639 | switch (reason) | ||
640 | { | ||
641 | case GNUNET_FS_DIRSCANNER_FILE_START: | ||
642 | if (verbose > 1) | ||
643 | { | ||
644 | if (is_directory == GNUNET_YES) | ||
645 | fprintf (stdout, _ ("Scanning directory `%s'.\n"), filename); | ||
646 | else | ||
647 | fprintf (stdout, _ ("Scanning file `%s'.\n"), filename); | ||
648 | } | ||
649 | break; | ||
650 | |||
651 | case GNUNET_FS_DIRSCANNER_FILE_IGNORED: | ||
652 | fprintf (stderr, | ||
653 | _ ("There was trouble processing file `%s', skipping it.\n"), | ||
654 | filename); | ||
655 | break; | ||
656 | |||
657 | case GNUNET_FS_DIRSCANNER_ALL_COUNTED: | ||
658 | if (verbose) | ||
659 | fprintf (stdout, "%s", _ ("Preprocessing complete.\n")); | ||
660 | break; | ||
661 | |||
662 | case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED: | ||
663 | if (verbose > 2) | ||
664 | fprintf (stdout, | ||
665 | _ ("Extracting meta data from file `%s' complete.\n"), | ||
666 | filename); | ||
667 | break; | ||
668 | |||
669 | case GNUNET_FS_DIRSCANNER_FINISHED: | ||
670 | if (verbose > 1) | ||
671 | fprintf (stdout, "%s", _ ("Meta data extraction has finished.\n")); | ||
672 | directory_scan_result = GNUNET_FS_directory_scan_get_result (ds); | ||
673 | ds = NULL; | ||
674 | GNUNET_FS_share_tree_trim (directory_scan_result); | ||
675 | directory_trim_complete (directory_scan_result); | ||
676 | break; | ||
677 | |||
678 | case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR: | ||
679 | fprintf (stdout, "%s", _ ("Error scanning directory.\n")); | ||
680 | ret = 1; | ||
681 | GNUNET_SCHEDULER_shutdown (); | ||
682 | break; | ||
683 | |||
684 | default: | ||
685 | GNUNET_assert (0); | ||
686 | break; | ||
687 | } | ||
688 | fflush (stdout); | ||
689 | } | ||
690 | |||
691 | |||
692 | /** | ||
693 | * Continuation proceeding with initialization after identity subsystem | ||
694 | * has been initialized. | ||
695 | * | ||
696 | * @param args0 filename to publish | ||
697 | */ | ||
698 | static void | ||
699 | identity_continuation (const char *args0) | ||
700 | { | ||
701 | char *ex; | ||
702 | char *emsg; | ||
703 | |||
704 | if ((NULL != pseudonym) && (NULL == namespace)) | ||
705 | { | ||
706 | fprintf (stderr, _ ("Selected pseudonym `%s' unknown\n"), pseudonym); | ||
707 | ret = 1; | ||
708 | GNUNET_SCHEDULER_shutdown (); | ||
709 | return; | ||
710 | } | ||
711 | if (NULL != uri_string) | ||
712 | { | ||
713 | emsg = NULL; | ||
714 | if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg))) | ||
715 | { | ||
716 | fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg); | ||
717 | GNUNET_free (emsg); | ||
718 | ret = 1; | ||
719 | GNUNET_SCHEDULER_shutdown (); | ||
720 | return; | ||
721 | } | ||
722 | GNUNET_FS_publish_ksk (ctx, | ||
723 | topKeywords, | ||
724 | meta, | ||
725 | uri, | ||
726 | &bo, | ||
727 | GNUNET_FS_PUBLISH_OPTION_NONE, | ||
728 | &uri_ksk_continuation, | ||
729 | NULL); | ||
730 | return; | ||
731 | } | ||
732 | if (GNUNET_OK != | ||
733 | GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex)) | ||
734 | ex = NULL; | ||
735 | if (0 != access (args0, R_OK)) | ||
736 | { | ||
737 | fprintf (stderr, | ||
738 | _ ("Failed to access `%s': %s\n"), | ||
739 | args0, | ||
740 | strerror (errno)); | ||
741 | GNUNET_free (ex); | ||
742 | return; | ||
743 | } | ||
744 | ds = GNUNET_FS_directory_scan_start (args0, | ||
745 | disable_extractor, | ||
746 | ex, | ||
747 | &directory_scan_cb, | ||
748 | NULL); | ||
749 | if (NULL == ds) | ||
750 | { | ||
751 | fprintf ( | ||
752 | stderr, | ||
753 | "%s", | ||
754 | _ ( | ||
755 | "Failed to start meta directory scanner. Is gnunet-helper-publish-fs installed?\n")); | ||
756 | GNUNET_free (ex); | ||
757 | return; | ||
758 | } | ||
759 | GNUNET_free (ex); | ||
760 | } | ||
761 | |||
762 | |||
763 | /** | ||
764 | * Function called by identity service with known pseudonyms. | ||
765 | * | ||
766 | * @param cls closure with 'const char *' of filename to publish | ||
767 | * @param ego ego handle | ||
768 | * @param ctx context for application to store data for this ego | ||
769 | * (during the lifetime of this process, initially NULL) | ||
770 | * @param name name assigned by the user for this ego, | ||
771 | * NULL if the user just deleted the ego and it | ||
772 | * must thus no longer be used | ||
773 | */ | ||
774 | static void | ||
775 | identity_cb (void *cls, | ||
776 | struct GNUNET_IDENTITY_Ego *ego, | ||
777 | void **ctx, | ||
778 | const char *name) | ||
779 | { | ||
780 | const char *args0 = cls; | ||
781 | |||
782 | if (NULL == ego) | ||
783 | { | ||
784 | identity_continuation (args0); | ||
785 | return; | ||
786 | } | ||
787 | if (NULL == name) | ||
788 | return; | ||
789 | if (0 == strcmp (name, pseudonym)) | ||
790 | namespace = ego; | ||
791 | } | ||
792 | |||
793 | |||
794 | /** | ||
795 | * Main function that will be run by the scheduler. | ||
796 | * | ||
797 | * @param cls closure | ||
798 | * @param args remaining command-line arguments | ||
799 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
800 | * @param c configuration | ||
801 | */ | ||
802 | static void | ||
803 | run (void *cls, | ||
804 | char *const *args, | ||
805 | const char *cfgfile, | ||
806 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
807 | { | ||
808 | /* check arguments */ | ||
809 | if ((NULL != uri_string) && (extract_only)) | ||
810 | { | ||
811 | printf (_ ("Cannot extract metadata from a URI!\n")); | ||
812 | ret = -1; | ||
813 | return; | ||
814 | } | ||
815 | if (((NULL == uri_string) || (extract_only)) && | ||
816 | ((NULL == args[0]) || (NULL != args[1]))) | ||
817 | { | ||
818 | printf (_ ("You must specify one and only one filename for insertion.\n")); | ||
819 | ret = -1; | ||
820 | return; | ||
821 | } | ||
822 | if ((NULL != uri_string) && (NULL != args[0])) | ||
823 | { | ||
824 | printf (_ ("You must NOT specify an URI and a filename.\n")); | ||
825 | ret = -1; | ||
826 | return; | ||
827 | } | ||
828 | if (NULL != pseudonym) | ||
829 | { | ||
830 | if (NULL == this_id) | ||
831 | { | ||
832 | fprintf (stderr, | ||
833 | _ ("Option `%s' is required when using option `%s'.\n"), | ||
834 | "-t", | ||
835 | "-P"); | ||
836 | ret = -1; | ||
837 | return; | ||
838 | } | ||
839 | } | ||
840 | else | ||
841 | { /* ordinary insertion checks */ | ||
842 | if (NULL != next_id) | ||
843 | { | ||
844 | fprintf (stderr, | ||
845 | _ ("Option `%s' makes no sense without option `%s'.\n"), | ||
846 | "-N", | ||
847 | "-P"); | ||
848 | ret = -1; | ||
849 | return; | ||
850 | } | ||
851 | if (NULL != this_id) | ||
852 | { | ||
853 | fprintf (stderr, | ||
854 | _ ("Option `%s' makes no sense without option `%s'.\n"), | ||
855 | "-t", | ||
856 | "-P"); | ||
857 | ret = -1; | ||
858 | return; | ||
859 | } | ||
860 | } | ||
861 | cfg = c; | ||
862 | ctx = GNUNET_FS_start (cfg, | ||
863 | "gnunet-publish", | ||
864 | &progress_cb, | ||
865 | NULL, | ||
866 | GNUNET_FS_FLAGS_NONE, | ||
867 | GNUNET_FS_OPTIONS_END); | ||
868 | if (NULL == ctx) | ||
869 | { | ||
870 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
871 | ret = 1; | ||
872 | return; | ||
873 | } | ||
874 | GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL); | ||
875 | if (NULL != pseudonym) | ||
876 | identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, args[0]); | ||
877 | else | ||
878 | identity_continuation (args[0]); | ||
879 | } | ||
880 | |||
881 | |||
882 | /** | ||
883 | * The main function to publish content to GNUnet. | ||
884 | * | ||
885 | * @param argc number of arguments from the command line | ||
886 | * @param argv command line arguments | ||
887 | * @return 0 ok, 1 on error | ||
888 | */ | ||
889 | int | ||
890 | main (int argc, char *const *argv) | ||
891 | { | ||
892 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
893 | { GNUNET_GETOPT_option_uint ('a', | ||
894 | "anonymity", | ||
895 | "LEVEL", | ||
896 | gettext_noop ( | ||
897 | "set the desired LEVEL of sender-anonymity"), | ||
898 | &bo.anonymity_level), | ||
899 | GNUNET_GETOPT_option_flag ( | ||
900 | 'D', | ||
901 | "disable-extractor", | ||
902 | gettext_noop ("do not use libextractor to add keywords or metadata"), | ||
903 | &disable_extractor), | ||
904 | GNUNET_GETOPT_option_flag ('E', | ||
905 | "enable-creation-time", | ||
906 | gettext_noop ( | ||
907 | "enable adding the creation time to the " | ||
908 | "metadata of the uploaded file"), | ||
909 | &enable_creation_time), | ||
910 | GNUNET_GETOPT_option_flag ('e', | ||
911 | "extract", | ||
912 | gettext_noop ( | ||
913 | "print list of extracted keywords that would " | ||
914 | "be used, but do not perform upload"), | ||
915 | &extract_only), | ||
916 | GNUNET_FS_GETOPT_KEYWORDS ( | ||
917 | 'k', | ||
918 | "key", | ||
919 | "KEYWORD", | ||
920 | gettext_noop ( | ||
921 | "add an additional keyword for the top-level " | ||
922 | "file or directory (this option can be specified multiple times)"), | ||
923 | &topKeywords), | ||
924 | GNUNET_FS_GETOPT_METADATA ( | ||
925 | 'm', | ||
926 | "meta", | ||
927 | "TYPE:VALUE", | ||
928 | gettext_noop ("set the meta-data for the given TYPE to the given VALUE"), | ||
929 | &meta), | ||
930 | GNUNET_GETOPT_option_flag ( | ||
931 | 'n', | ||
932 | "noindex", | ||
933 | gettext_noop ("do not index, perform full insertion (stores " | ||
934 | "entire file in encrypted form in GNUnet database)"), | ||
935 | &do_insert), | ||
936 | GNUNET_GETOPT_option_string ( | ||
937 | 'N', | ||
938 | "next", | ||
939 | "ID", | ||
940 | gettext_noop ("specify ID of an updated version to be " | ||
941 | "published in the future (for namespace insertions only)"), | ||
942 | &next_id), | ||
943 | GNUNET_GETOPT_option_uint ('p', | ||
944 | "priority", | ||
945 | "PRIORITY", | ||
946 | gettext_noop ( | ||
947 | "specify the priority of the content"), | ||
948 | &bo.content_priority), | ||
949 | GNUNET_GETOPT_option_string ('P', | ||
950 | "pseudonym", | ||
951 | "NAME", | ||
952 | gettext_noop ( | ||
953 | "publish the files under the pseudonym " | ||
954 | "NAME (place file into namespace)"), | ||
955 | &pseudonym), | ||
956 | GNUNET_GETOPT_option_uint ('r', | ||
957 | "replication", | ||
958 | "LEVEL", | ||
959 | gettext_noop ( | ||
960 | "set the desired replication LEVEL"), | ||
961 | &bo.replication_level), | ||
962 | GNUNET_GETOPT_option_flag ('s', | ||
963 | "simulate-only", | ||
964 | gettext_noop ( | ||
965 | "only simulate the process but do not do " | ||
966 | "any actual publishing (useful to compute URIs)"), | ||
967 | &do_simulate), | ||
968 | GNUNET_GETOPT_option_string ('t', | ||
969 | "this", | ||
970 | "ID", | ||
971 | gettext_noop ( | ||
972 | "set the ID of this version of the publication " | ||
973 | "(for namespace insertions only)"), | ||
974 | &this_id), | ||
975 | GNUNET_GETOPT_option_string ( | ||
976 | 'u', | ||
977 | "uri", | ||
978 | "URI", | ||
979 | gettext_noop ( | ||
980 | "URI to be published (can be used instead of passing a " | ||
981 | "file to add keywords to the file with the respective URI)"), | ||
982 | &uri_string), | ||
983 | |||
984 | GNUNET_GETOPT_option_verbose (&verbose), | ||
985 | |||
986 | GNUNET_GETOPT_OPTION_END }; | ||
987 | |||
988 | bo.expiration_time = | ||
989 | GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2); | ||
990 | |||
991 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
992 | return 2; | ||
993 | ret = | ||
994 | (GNUNET_OK == | ||
995 | GNUNET_PROGRAM_run (argc, | ||
996 | argv, | ||
997 | "gnunet-publish [OPTIONS] FILENAME", | ||
998 | gettext_noop ("Publish a file or directory on GNUnet"), | ||
999 | options, | ||
1000 | &run, | ||
1001 | NULL)) | ||
1002 | ? ret | ||
1003 | : 1; | ||
1004 | GNUNET_free_nz ((void *) argv); | ||
1005 | return ret; | ||
1006 | } | ||
1007 | |||
1008 | |||
1009 | /* end of gnunet-publish.c */ | ||
diff --git a/src/cli/fs/gnunet-search.c b/src/cli/fs/gnunet-search.c new file mode 100644 index 000000000..a72cf97cc --- /dev/null +++ b/src/cli/fs/gnunet-search.c | |||
@@ -0,0 +1,801 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 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 fs/gnunet-search.c | ||
22 | * @brief searching for files on GNUnet | ||
23 | * @author Christian Grothoff | ||
24 | * @author Krista Bennett | ||
25 | * @author James Blackwell | ||
26 | * @author Igor Wronsky | ||
27 | * @author madmurphy | ||
28 | */ | ||
29 | #include "platform.h" | ||
30 | #include <ctype.h> | ||
31 | #include <inttypes.h> | ||
32 | #include <limits.h> | ||
33 | |||
34 | #include "gnunet_fs_service.h" | ||
35 | |||
36 | |||
37 | #define GNUNET_SEARCH_log(kind, ...) \ | ||
38 | GNUNET_log_from (kind, "gnunet-search", __VA_ARGS__) | ||
39 | |||
40 | |||
41 | /* The default settings that we use for the printed output */ | ||
42 | |||
43 | #define DEFAULT_DIR_FORMAT "#%n:\ngnunet-download -o \"%f\" -R %u\n\n" | ||
44 | #define HELP_DEFAULT_DIR_FORMAT "#%n:\\ngnunet-download -o \"%f\" -R %u\\n\\n" | ||
45 | #define DEFAULT_FILE_FORMAT "#%n:\ngnunet-download -o \"%f\" %u\n\n" | ||
46 | #define HELP_DEFAULT_FILE_FORMAT "#%n:\\ngnunet-download -o \"%f\" %u\\n\\n" | ||
47 | #define VERB_DEFAULT_DIR_FORMAT DEFAULT_DIR_FORMAT "%a\n" | ||
48 | #define VERB_DEFAULT_FILE_FORMAT DEFAULT_FILE_FORMAT "%a\n" | ||
49 | |||
50 | #if HAVE_LIBEXTRACTOR | ||
51 | #define DEFAULT_META_FORMAT " %t: %p\n" | ||
52 | #define HELP_DEFAULT_META_FORMAT " %t: %p\\n" | ||
53 | #define HELP_EXTRACTOR_TEXTADD ", %t" | ||
54 | #else | ||
55 | #define DEFAULT_META_FORMAT " MetaType #%i: %p\n" | ||
56 | #define HELP_DEFAULT_META_FORMAT " MetaType #%i: %p\\n" | ||
57 | #define HELP_EXTRACTOR_TEXTADD "" | ||
58 | #endif | ||
59 | |||
60 | #define GENERIC_DIRECTORY_NAME "collection" | ||
61 | #define GENERIC_FILE_NAME "no-name" | ||
62 | #define GENERIC_FILE_MIMETYPE "application/octet-stream" | ||
63 | |||
64 | |||
65 | enum GNUNET_SEARCH_MetadataPrinterFlags | ||
66 | { | ||
67 | METADATA_PRINTER_FLAG_NONE = 0, | ||
68 | METADATA_PRINTER_FLAG_ONE_RUN = 1, | ||
69 | METADATA_PRINTER_FLAG_HAVE_TYPE = 2 | ||
70 | }; | ||
71 | |||
72 | |||
73 | struct GNUNET_SEARCH_MetadataPrinterInfo | ||
74 | { | ||
75 | unsigned int counter; | ||
76 | unsigned int flags; | ||
77 | int type; | ||
78 | }; | ||
79 | |||
80 | |||
81 | static int ret; | ||
82 | |||
83 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
84 | |||
85 | static struct GNUNET_FS_Handle *ctx; | ||
86 | |||
87 | static struct GNUNET_FS_SearchContext *sc; | ||
88 | |||
89 | static char *output_filename; | ||
90 | |||
91 | static char *format_string; | ||
92 | |||
93 | static char *dir_format_string; | ||
94 | |||
95 | static char *meta_format_string; | ||
96 | |||
97 | static struct GNUNET_FS_DirectoryBuilder *db; | ||
98 | |||
99 | static unsigned int anonymity = 1; | ||
100 | |||
101 | /** | ||
102 | * Timeout for the search, 0 means to wait for CTRL-C. | ||
103 | */ | ||
104 | static struct GNUNET_TIME_Relative timeout; | ||
105 | |||
106 | static unsigned int results_limit; | ||
107 | |||
108 | static unsigned int results; | ||
109 | |||
110 | static unsigned int verbose; | ||
111 | |||
112 | static int bookmark_only; | ||
113 | |||
114 | static int local_only; | ||
115 | |||
116 | static int silent_mode; | ||
117 | |||
118 | static struct GNUNET_SCHEDULER_Task *tt; | ||
119 | |||
120 | static int stop_searching; | ||
121 | |||
122 | |||
123 | /** | ||
124 | * Print the escape sequence at the beginning of a string. | ||
125 | * | ||
126 | * @param esc a string that **must** begin with a backslash (the function only | ||
127 | * assumes that it does, but does not check) | ||
128 | * @return the fragment that follows what has been printed | ||
129 | * @author madmurphy | ||
130 | * | ||
131 | * If `"\\nfoo"` is passed as argument, this function prints a new line and | ||
132 | * returns `"foo"` | ||
133 | */ | ||
134 | static const char * | ||
135 | print_escape_sequence (const char *const esc) | ||
136 | { | ||
137 | unsigned int probe; | ||
138 | const char *cursor = esc + 1; | ||
139 | char tmp; | ||
140 | switch (*cursor) | ||
141 | { | ||
142 | /* Trivia */ | ||
143 | case '\\': putchar ('\\'); return cursor + 1; | ||
144 | case 'a': putchar ('\a'); return cursor + 1; | ||
145 | case 'b': putchar ('\b'); return cursor + 1; | ||
146 | case 'e': putchar ('\x1B'); return cursor + 1; | ||
147 | case 'f': putchar ('\f'); return cursor + 1; | ||
148 | case 'n': putchar ('\n'); return cursor + 1; | ||
149 | case 'r': putchar ('\r'); return cursor + 1; | ||
150 | case 't': putchar ('\t'); return cursor + 1; | ||
151 | case 'v': putchar ('\v'); return cursor + 1; | ||
152 | |||
153 | /* Possibly hexadecimal code point */ | ||
154 | case 'x': | ||
155 | probe = 0; | ||
156 | while (probe < 256 && isxdigit ((tmp = *++cursor))) | ||
157 | probe = (probe << 4) + tmp - (tmp > 96 ? 87 : tmp > 64 ? 55 : 48); | ||
158 | goto maybe_codepoint; | ||
159 | |||
160 | /* Possibly octal code point */ | ||
161 | case '0': case '1': case '2': case '3': | ||
162 | case '4': case '5': case '6': case '7': | ||
163 | probe = *cursor++ - 48; | ||
164 | do probe = (probe << 3) + *cursor++ - 48; | ||
165 | while (probe < 256 && cursor < esc + 4 && *cursor > 47 && *cursor < 56); | ||
166 | goto maybe_codepoint; | ||
167 | |||
168 | /* Boredom */ | ||
169 | case '\0': putchar ('\\'); return cursor; | ||
170 | default: printf ("\\%c", *cursor); return cursor + 1; | ||
171 | } | ||
172 | |||
173 | maybe_codepoint: | ||
174 | if (probe < 256) | ||
175 | putchar (probe); | ||
176 | else | ||
177 | fwrite (esc, 1, cursor - esc, stdout); | ||
178 | return cursor; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Type of a function that libextractor calls for each | ||
184 | * meta data item found. | ||
185 | * | ||
186 | * @param cls closure (user-defined, used for the iteration info) | ||
187 | * @param plugin_name name of the plugin that produced this value; | ||
188 | * special values can be used (e.g. '<zlib>' for zlib being | ||
189 | * used in the main libextractor library and yielding | ||
190 | * meta data). | ||
191 | * @param type libextractor-type describing the meta data | ||
192 | * @param format basic format information about data | ||
193 | * @param data_mime_type mime-type of data (not of the original file); | ||
194 | * can be NULL (if mime-type is not known) | ||
195 | * @param data actual meta-data found | ||
196 | * @param data_size number of bytes in @a data | ||
197 | * @return 0 to continue extracting, 1 to abort | ||
198 | */ | ||
199 | static int | ||
200 | item_printer (void *const cls, | ||
201 | const char *const plugin_name, | ||
202 | const enum EXTRACTOR_MetaType type, | ||
203 | const enum EXTRACTOR_MetaFormat format, | ||
204 | const char *const data_mime_type, | ||
205 | const char *const data, | ||
206 | const size_t data_size) | ||
207 | { | ||
208 | #define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls) | ||
209 | if ((format != EXTRACTOR_METAFORMAT_UTF8 && | ||
210 | format != EXTRACTOR_METAFORMAT_C_STRING) || | ||
211 | type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) | ||
212 | return 0; | ||
213 | info->counter++; | ||
214 | if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type) | ||
215 | return 0; | ||
216 | |||
217 | const char *cursor = meta_format_string; | ||
218 | const char *next_spec = strchr (cursor, '%'); | ||
219 | const char *next_esc = strchr (cursor, '\\'); | ||
220 | |||
221 | parse_format: | ||
222 | |||
223 | /* If an escape sequence exists before the next format specifier... */ | ||
224 | if (next_esc && (! next_spec || next_esc < next_spec)) | ||
225 | { | ||
226 | if (next_esc > cursor) | ||
227 | fwrite (cursor, 1, next_esc - cursor, stdout); | ||
228 | |||
229 | cursor = print_escape_sequence (next_esc); | ||
230 | next_esc = strchr (cursor, '\\'); | ||
231 | goto parse_format; | ||
232 | } | ||
233 | |||
234 | /* If a format specifier exists before the next escape sequence... */ | ||
235 | if (next_spec && (! next_esc || next_spec < next_esc)) | ||
236 | { | ||
237 | if (next_spec > cursor) | ||
238 | fwrite (cursor, 1, next_spec - cursor, stdout); | ||
239 | |||
240 | switch (*++next_spec) | ||
241 | { | ||
242 | case '%': putchar ('%'); break; | ||
243 | case 'i': printf ("%d", type); break; | ||
244 | case 'l': printf ("%lu", (long unsigned int) data_size); break; | ||
245 | case 'n': printf ("%u", info->counter); break; | ||
246 | case 'p': printf ("%s", data); break; | ||
247 | #if HAVE_LIBEXTRACTOR | ||
248 | case 't': | ||
249 | printf ("%s", | ||
250 | dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, | ||
251 | EXTRACTOR_metatype_to_string (type))); | ||
252 | break; | ||
253 | #endif | ||
254 | case 'w': printf ("%s", plugin_name); break; | ||
255 | case '\0': putchar ('%'); return 0; | ||
256 | default: printf ("%%%c", *next_spec); break; | ||
257 | } | ||
258 | cursor = next_spec + 1; | ||
259 | next_spec = strchr (cursor, '%'); | ||
260 | goto parse_format; | ||
261 | } | ||
262 | |||
263 | if (*cursor) | ||
264 | printf ("%s", cursor); | ||
265 | |||
266 | return info->flags & METADATA_PRINTER_FLAG_ONE_RUN; | ||
267 | #undef info | ||
268 | } | ||
269 | |||
270 | |||
271 | /** | ||
272 | * Print a search result according to the current formats | ||
273 | * | ||
274 | * @param filename the filename for this result | ||
275 | * @param uri the `struct GNUNET_FS_Uri` this result refers to | ||
276 | * @param metadata the `struct GNUNET_FS_MetaData` associated with this | ||
277 | result | ||
278 | * @param resultnum the result number | ||
279 | * @param is_directory GNUNET_YES if this is a directory, otherwise GNUNET_NO | ||
280 | * @author madmurphy | ||
281 | */ | ||
282 | static void | ||
283 | print_search_result (const char *const filename, | ||
284 | const struct GNUNET_FS_Uri *const uri, | ||
285 | const struct GNUNET_FS_MetaData *const metadata, | ||
286 | const unsigned int resultnum, | ||
287 | const int is_directory) | ||
288 | { | ||
289 | |||
290 | const char *cursor = GNUNET_YES == is_directory ? | ||
291 | dir_format_string | ||
292 | : format_string; | ||
293 | |||
294 | const char *next_spec = strchr (cursor, '%'); | ||
295 | const char *next_esc = strchr (cursor, '\\'); | ||
296 | char *placeholder; | ||
297 | struct GNUNET_SEARCH_MetadataPrinterInfo info; | ||
298 | |||
299 | parse_format: | ||
300 | /* If an escape sequence exists before the next format specifier... */ | ||
301 | if (next_esc && (! next_spec || next_esc < next_spec)) | ||
302 | { | ||
303 | if (next_esc > cursor) | ||
304 | fwrite (cursor, 1, next_esc - cursor, stdout); | ||
305 | |||
306 | cursor = print_escape_sequence (next_esc); | ||
307 | next_esc = strchr (cursor, '\\'); | ||
308 | goto parse_format; | ||
309 | } | ||
310 | |||
311 | /* If a format specifier exists before the next escape sequence... */ | ||
312 | if (next_spec && (! next_esc || next_spec < next_esc)) | ||
313 | { | ||
314 | if (next_spec > cursor) | ||
315 | fwrite (cursor, 1, next_spec - cursor, stdout); | ||
316 | |||
317 | switch (*++next_spec) | ||
318 | { | ||
319 | /* All metadata fields */ | ||
320 | case 'a': | ||
321 | info.flags = METADATA_PRINTER_FLAG_NONE; | ||
322 | |||
323 | iterate_meta: | ||
324 | info.counter = 0; | ||
325 | GNUNET_FS_meta_data_iterate (metadata, &item_printer, &info); | ||
326 | break; | ||
327 | /* File's name */ | ||
328 | case 'f': | ||
329 | if (GNUNET_YES == is_directory) | ||
330 | { | ||
331 | printf ("%s%s", filename, GNUNET_FS_DIRECTORY_EXT); | ||
332 | break; | ||
333 | } | ||
334 | printf ("%s", filename); | ||
335 | break; | ||
336 | /* Only the first metadata field */ | ||
337 | case 'j': | ||
338 | info.flags = METADATA_PRINTER_FLAG_ONE_RUN; | ||
339 | goto iterate_meta; | ||
340 | /* File name's length */ | ||
341 | case 'l': | ||
342 | printf ("%lu", | ||
343 | (long unsigned int) (GNUNET_YES == is_directory ? | ||
344 | strlen (filename) | ||
345 | + (sizeof(GNUNET_FS_DIRECTORY_EXT) - 1) | ||
346 | : | ||
347 | strlen (filename))); | ||
348 | break; | ||
349 | /* File's mime type */ | ||
350 | case 'm': | ||
351 | if (GNUNET_YES == is_directory) | ||
352 | { | ||
353 | printf ("%s", GNUNET_FS_DIRECTORY_MIME); | ||
354 | break; | ||
355 | } | ||
356 | placeholder = GNUNET_FS_meta_data_get_by_type ( | ||
357 | metadata, | ||
358 | EXTRACTOR_METATYPE_MIMETYPE); | ||
359 | printf ("%s", placeholder ? placeholder : GENERIC_FILE_MIMETYPE); | ||
360 | GNUNET_free (placeholder); | ||
361 | break; | ||
362 | /* Result number */ | ||
363 | case 'n': printf ("%u", resultnum); break; | ||
364 | /* File's size */ | ||
365 | case 's': | ||
366 | printf ("%" PRIu64, GNUNET_FS_uri_chk_get_file_size (uri)); | ||
367 | break; | ||
368 | /* File's URI */ | ||
369 | case 'u': | ||
370 | placeholder = GNUNET_FS_uri_to_string (uri); | ||
371 | printf ("%s", placeholder); | ||
372 | GNUNET_free (placeholder); | ||
373 | break; | ||
374 | |||
375 | /* We can add as many cases as we want here... */ | ||
376 | |||
377 | /* Handle `%123#a` and `%123#j` (e.g. `%5#j` is a book title) */ | ||
378 | case '0': case '1': case '2': case '3': case '4': | ||
379 | case '5': case '6': case '7': case '8': case '9': | ||
380 | cursor = next_spec; | ||
381 | info.type = *cursor - 48; | ||
382 | while (isdigit (*++cursor) && info.type < (INT_MAX - *cursor + 48) / 10) | ||
383 | info.type = info.type * 10 + *cursor - 48; | ||
384 | if (info.type == 0 || *cursor != '#') | ||
385 | goto not_a_specifier; | ||
386 | switch (*++cursor) | ||
387 | { | ||
388 | /* All metadata fields of type `info.type` */ | ||
389 | case 'a': | ||
390 | next_spec = cursor; | ||
391 | info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE; | ||
392 | goto iterate_meta; | ||
393 | |||
394 | /* Only the first metadata field of type `info.type` */ | ||
395 | case 'j': | ||
396 | next_spec = cursor; | ||
397 | info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE | ||
398 | | METADATA_PRINTER_FLAG_ONE_RUN; | ||
399 | goto iterate_meta; | ||
400 | } | ||
401 | goto not_a_specifier; | ||
402 | |||
403 | /* All other cases */ | ||
404 | case '%': putchar ('%'); break; | ||
405 | case '\0': putchar ('%'); return; | ||
406 | |||
407 | not_a_specifier: | ||
408 | default: printf ("%%%c", *next_spec); break; | ||
409 | } | ||
410 | cursor = next_spec + 1; | ||
411 | next_spec = strchr (cursor, '%'); | ||
412 | goto parse_format; | ||
413 | } | ||
414 | |||
415 | if (*cursor) | ||
416 | printf ("%s", cursor); | ||
417 | } | ||
418 | |||
419 | |||
420 | static void | ||
421 | clean_task (void *const cls) | ||
422 | { | ||
423 | size_t dsize; | ||
424 | void *ddata; | ||
425 | |||
426 | GNUNET_FS_stop (ctx); | ||
427 | ctx = NULL; | ||
428 | if (output_filename == NULL) | ||
429 | return; | ||
430 | if (GNUNET_OK != | ||
431 | GNUNET_FS_directory_builder_finish (db, &dsize, &ddata)) | ||
432 | { | ||
433 | GNUNET_break (0); | ||
434 | GNUNET_free (output_filename); | ||
435 | return; | ||
436 | } | ||
437 | (void) GNUNET_DISK_directory_remove (output_filename); | ||
438 | if (GNUNET_OK != | ||
439 | GNUNET_DISK_fn_write (output_filename, | ||
440 | ddata, | ||
441 | dsize, | ||
442 | GNUNET_DISK_PERM_USER_READ | ||
443 | | GNUNET_DISK_PERM_USER_WRITE)) | ||
444 | { | ||
445 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
446 | _ ("Failed to write directory with search results to " | ||
447 | "`%s'\n"), | ||
448 | output_filename); | ||
449 | } | ||
450 | GNUNET_free (ddata); | ||
451 | GNUNET_free (output_filename); | ||
452 | } | ||
453 | |||
454 | |||
455 | /** | ||
456 | * Called by FS client to give information about the progress of an | ||
457 | * operation. | ||
458 | * | ||
459 | * @param cls closure | ||
460 | * @param info details about the event, specifying the event type | ||
461 | * and various bits about the event | ||
462 | * @return client-context (for the next progress call | ||
463 | * for this operation; should be set to NULL for | ||
464 | * SUSPEND and STOPPED events). The value returned | ||
465 | * will be passed to future callbacks in the respective | ||
466 | * field in the GNUNET_FS_ProgressInfo struct. | ||
467 | */ | ||
468 | static void * | ||
469 | progress_cb (void *const cls, | ||
470 | const struct GNUNET_FS_ProgressInfo *const info) | ||
471 | { | ||
472 | static unsigned int cnt; | ||
473 | int is_directory; | ||
474 | char *filename; | ||
475 | |||
476 | switch (info->status) | ||
477 | { | ||
478 | case GNUNET_FS_STATUS_SEARCH_START: | ||
479 | break; | ||
480 | |||
481 | case GNUNET_FS_STATUS_SEARCH_RESULT: | ||
482 | if (stop_searching) | ||
483 | break; | ||
484 | |||
485 | if (db != NULL) | ||
486 | GNUNET_FS_directory_builder_add ( | ||
487 | db, | ||
488 | info->value.search.specifics.result.uri, | ||
489 | info->value.search.specifics.result.meta, | ||
490 | NULL); | ||
491 | |||
492 | if (silent_mode) | ||
493 | break; | ||
494 | |||
495 | cnt++; | ||
496 | filename = GNUNET_FS_meta_data_get_by_type ( | ||
497 | info->value.search.specifics.result.meta, | ||
498 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
499 | is_directory = GNUNET_FS_meta_data_test_for_directory ( | ||
500 | info->value.search.specifics.result.meta); | ||
501 | if (NULL != filename) | ||
502 | { | ||
503 | while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1])) | ||
504 | filename[strlen (filename) - 1] = '\0'; | ||
505 | GNUNET_DISK_filename_canonicalize (filename); | ||
506 | } | ||
507 | print_search_result (filename ? | ||
508 | filename | ||
509 | : is_directory ? | ||
510 | GENERIC_DIRECTORY_NAME | ||
511 | : | ||
512 | GENERIC_FILE_NAME, | ||
513 | info->value.search.specifics.result.uri, | ||
514 | info->value.search.specifics.result.meta, | ||
515 | cnt, | ||
516 | is_directory); | ||
517 | fflush (stdout); | ||
518 | GNUNET_free (filename); | ||
519 | results++; | ||
520 | if ((results_limit > 0) && (results >= results_limit)) | ||
521 | { | ||
522 | GNUNET_SCHEDULER_shutdown (); | ||
523 | /* otherwise the function might keep printing results for a while... */ | ||
524 | stop_searching = GNUNET_YES; | ||
525 | } | ||
526 | break; | ||
527 | |||
528 | case GNUNET_FS_STATUS_SEARCH_UPDATE: | ||
529 | case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: | ||
530 | /* ignore */ | ||
531 | break; | ||
532 | |||
533 | case GNUNET_FS_STATUS_SEARCH_ERROR: | ||
534 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
535 | _ ("Error searching: %s.\n"), | ||
536 | info->value.search.specifics.error.message); | ||
537 | GNUNET_SCHEDULER_shutdown (); | ||
538 | break; | ||
539 | |||
540 | case GNUNET_FS_STATUS_SEARCH_STOPPED: | ||
541 | GNUNET_SCHEDULER_add_now (&clean_task, NULL); | ||
542 | break; | ||
543 | |||
544 | default: | ||
545 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
546 | _ ("Unexpected status: %d\n"), | ||
547 | info->status); | ||
548 | break; | ||
549 | } | ||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | |||
554 | static void | ||
555 | shutdown_task (void *const cls) | ||
556 | { | ||
557 | if (sc != NULL) | ||
558 | { | ||
559 | GNUNET_FS_search_stop (sc); | ||
560 | sc = NULL; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | |||
565 | static void | ||
566 | timeout_task (void *const cls) | ||
567 | { | ||
568 | tt = NULL; | ||
569 | stop_searching = GNUNET_YES; | ||
570 | GNUNET_SCHEDULER_shutdown (); | ||
571 | } | ||
572 | |||
573 | |||
574 | /** | ||
575 | * Main function that will be run by the scheduler. | ||
576 | * | ||
577 | * @param cls closure | ||
578 | * @param args remaining command-line arguments | ||
579 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
580 | * @param cfgarg configuration | ||
581 | */ | ||
582 | static void | ||
583 | run (void *const cls, | ||
584 | char *const *const args, | ||
585 | const char *const cfgfile, | ||
586 | const struct GNUNET_CONFIGURATION_Handle *const cfgarg) | ||
587 | { | ||
588 | struct GNUNET_FS_Uri *uri; | ||
589 | unsigned int argc; | ||
590 | enum GNUNET_FS_SearchOptions options; | ||
591 | |||
592 | if (silent_mode && bookmark_only) | ||
593 | { | ||
594 | fprintf (stderr, | ||
595 | _ ("Conflicting options --bookmark-only and --silent.\n")); | ||
596 | ret = 1; | ||
597 | return; | ||
598 | } | ||
599 | if (bookmark_only && output_filename) | ||
600 | { | ||
601 | fprintf (stderr, | ||
602 | _ ("Conflicting options --bookmark-only and --output.\n")); | ||
603 | ret = 1; | ||
604 | return; | ||
605 | } | ||
606 | if (silent_mode && ! output_filename) | ||
607 | { | ||
608 | fprintf (stderr, _ ("An output file is mandatory for silent mode.\n")); | ||
609 | ret = 1; | ||
610 | return; | ||
611 | } | ||
612 | if (NULL == dir_format_string) | ||
613 | dir_format_string = format_string ? format_string | ||
614 | : verbose ? VERB_DEFAULT_DIR_FORMAT | ||
615 | : DEFAULT_DIR_FORMAT; | ||
616 | if (NULL == format_string) | ||
617 | format_string = verbose ? VERB_DEFAULT_FILE_FORMAT | ||
618 | : DEFAULT_FILE_FORMAT; | ||
619 | if (NULL == meta_format_string) | ||
620 | meta_format_string = DEFAULT_META_FORMAT; | ||
621 | argc = 0; | ||
622 | while (NULL != args[argc]) | ||
623 | argc++; | ||
624 | uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args); | ||
625 | if (NULL == uri) | ||
626 | { | ||
627 | fprintf (stderr, | ||
628 | "%s", | ||
629 | _ ("Could not create keyword URI from arguments.\n")); | ||
630 | ret = 1; | ||
631 | return; | ||
632 | } | ||
633 | if (! GNUNET_FS_uri_test_ksk (uri) && ! GNUNET_FS_uri_test_sks (uri)) | ||
634 | { | ||
635 | fprintf (stderr, | ||
636 | "%s", | ||
637 | _ ("Invalid URI. Valid URIs for searching are keyword query " | ||
638 | "URIs\n(\"gnunet://fs/ksk/...\") and namespace content URIs " | ||
639 | "(\"gnunet://fs/sks/...\").\n")); | ||
640 | GNUNET_FS_uri_destroy (uri); | ||
641 | ret = 1; | ||
642 | return; | ||
643 | } | ||
644 | if (bookmark_only) | ||
645 | { | ||
646 | char *bmstr = GNUNET_FS_uri_to_string (uri); | ||
647 | printf ("%s\n", bmstr); | ||
648 | GNUNET_free (bmstr); | ||
649 | GNUNET_FS_uri_destroy (uri); | ||
650 | ret = 0; | ||
651 | return; | ||
652 | } | ||
653 | cfg = cfgarg; | ||
654 | ctx = GNUNET_FS_start (cfg, | ||
655 | "gnunet-search", | ||
656 | &progress_cb, | ||
657 | NULL, | ||
658 | GNUNET_FS_FLAGS_NONE, | ||
659 | GNUNET_FS_OPTIONS_END); | ||
660 | if (NULL == ctx) | ||
661 | { | ||
662 | fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS"); | ||
663 | GNUNET_FS_uri_destroy (uri); | ||
664 | ret = 1; | ||
665 | return; | ||
666 | } | ||
667 | if (output_filename != NULL) | ||
668 | db = GNUNET_FS_directory_builder_create (NULL); | ||
669 | options = GNUNET_FS_SEARCH_OPTION_NONE; | ||
670 | if (local_only) | ||
671 | options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY; | ||
672 | sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL); | ||
673 | GNUNET_FS_uri_destroy (uri); | ||
674 | if (NULL == sc) | ||
675 | { | ||
676 | fprintf (stderr, "%s", _ ("Could not start searching.\n")); | ||
677 | GNUNET_FS_stop (ctx); | ||
678 | ret = 1; | ||
679 | return; | ||
680 | } | ||
681 | if (0 != timeout.rel_value_us) | ||
682 | tt = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, NULL); | ||
683 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
684 | } | ||
685 | |||
686 | |||
687 | /** | ||
688 | * The main function to search GNUnet. | ||
689 | * | ||
690 | * @param argc number of arguments from the command line | ||
691 | * @param argv command line arguments | ||
692 | * @return 0 ok, an error number on error | ||
693 | */ | ||
694 | int | ||
695 | main (int argc, char *const *argv) | ||
696 | { | ||
697 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
698 | { GNUNET_GETOPT_option_uint ( | ||
699 | 'a', | ||
700 | "anonymity", | ||
701 | "LEVEL", | ||
702 | gettext_noop ("set the desired LEVEL of receiver-anonymity (default: " | ||
703 | "1)"), | ||
704 | &anonymity), | ||
705 | GNUNET_GETOPT_option_flag ( | ||
706 | 'b', | ||
707 | "bookmark-only", | ||
708 | gettext_noop ("do not search, print only the URI that points to this " | ||
709 | "search"), | ||
710 | &bookmark_only), | ||
711 | GNUNET_GETOPT_option_string ( | ||
712 | 'F', | ||
713 | "dir-printf", | ||
714 | "FORMAT", | ||
715 | gettext_noop ("write search results for directories according to " | ||
716 | "FORMAT; accepted placeholders are: %a, %f, %j, %l, %m, " | ||
717 | "%n, %s; defaults to the value of --printf when omitted " | ||
718 | "or to `" HELP_DEFAULT_DIR_FORMAT "` if --printf is " | ||
719 | "omitted too"), | ||
720 | &dir_format_string), | ||
721 | GNUNET_GETOPT_option_string ( | ||
722 | 'f', | ||
723 | "printf", | ||
724 | "FORMAT", | ||
725 | gettext_noop ("write search results according to FORMAT; accepted " | ||
726 | "placeholders are: %a, %f, %j, %l, %m, %n, %s; defaults " | ||
727 | "to `" HELP_DEFAULT_FILE_FORMAT "` when omitted"), | ||
728 | &format_string), | ||
729 | GNUNET_GETOPT_option_string ( | ||
730 | 'i', | ||
731 | "iter-printf", | ||
732 | "FORMAT", | ||
733 | gettext_noop ("when the %a or %j placeholders appear in --printf or " | ||
734 | "--dir-printf, list each metadata property according to " | ||
735 | "FORMAT; accepted placeholders are: %i, %l, %n, %p" | ||
736 | HELP_EXTRACTOR_TEXTADD ", %w; defaults to `" | ||
737 | HELP_DEFAULT_META_FORMAT "` when omitted"), | ||
738 | &meta_format_string), | ||
739 | GNUNET_GETOPT_option_uint ('N', | ||
740 | "results", | ||
741 | "VALUE", | ||
742 | gettext_noop ("automatically terminate search " | ||
743 | "after VALUE results are found"), | ||
744 | &results_limit), | ||
745 | GNUNET_GETOPT_option_flag ( | ||
746 | 'n', | ||
747 | "no-network", | ||
748 | gettext_noop ("only search the local peer (no P2P network search)"), | ||
749 | &local_only), | ||
750 | GNUNET_GETOPT_option_string ( | ||
751 | 'o', | ||
752 | "output", | ||
753 | "FILENAME", | ||
754 | gettext_noop ("create a GNUnet directory with search results at " | ||
755 | "FILENAME (e.g. `gnunet-search --output=commons" | ||
756 | GNUNET_FS_DIRECTORY_EXT " commons`)"), | ||
757 | &output_filename), | ||
758 | GNUNET_GETOPT_option_flag ( | ||
759 | 's', | ||
760 | "silent", | ||
761 | gettext_noop ("silent mode (requires the --output argument)"), | ||
762 | &silent_mode), | ||
763 | GNUNET_GETOPT_option_relative_time ( | ||
764 | 't', | ||
765 | "timeout", | ||
766 | "DELAY", | ||
767 | gettext_noop ("automatically terminate search after DELAY; the value " | ||
768 | "given must be a number followed by a space and a time " | ||
769 | "unit, for example \"500 ms\"; without a unit it defaults " | ||
770 | "to microseconds - 1000000 = 1 second; if 0 or omitted " | ||
771 | "it means to wait for CTRL-C"), | ||
772 | &timeout), | ||
773 | GNUNET_GETOPT_option_increment_uint ( | ||
774 | 'V', | ||
775 | "verbose", | ||
776 | gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and " | ||
777 | "--dir-printf arguments - ignored when these are provided " | ||
778 | "by the user)"), | ||
779 | &verbose), | ||
780 | GNUNET_GETOPT_OPTION_END }; | ||
781 | |||
782 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
783 | return 12; | ||
784 | |||
785 | if (GNUNET_SYSERR == | ||
786 | GNUNET_PROGRAM_run (argc, | ||
787 | argv, | ||
788 | "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...", | ||
789 | gettext_noop ("Search for files that have been " | ||
790 | "published on GNUnet\n"), | ||
791 | options, | ||
792 | &run, | ||
793 | NULL)) | ||
794 | ret = 1; | ||
795 | |||
796 | GNUNET_free_nz ((void *) argv); | ||
797 | return ret; | ||
798 | } | ||
799 | |||
800 | |||
801 | /* end of gnunet-search.c */ | ||
diff --git a/src/cli/fs/gnunet-unindex.c b/src/cli/fs/gnunet-unindex.c new file mode 100644 index 000000000..326f75a63 --- /dev/null +++ b/src/cli/fs/gnunet-unindex.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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 fs/gnunet-unindex.c | ||
22 | * @brief unindex files published on GNUnet | ||
23 | * @author Christian Grothoff | ||
24 | * @author Krista Bennett | ||
25 | * @author James Blackwell | ||
26 | * @author Igor Wronsky | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
37 | |||
38 | static struct GNUNET_FS_Handle *ctx; | ||
39 | |||
40 | static struct GNUNET_FS_UnindexContext *uc; | ||
41 | |||
42 | |||
43 | static void | ||
44 | cleanup_task (void *cls) | ||
45 | { | ||
46 | GNUNET_FS_stop (ctx); | ||
47 | ctx = NULL; | ||
48 | } | ||
49 | |||
50 | |||
51 | static void | ||
52 | shutdown_task (void *cls) | ||
53 | { | ||
54 | struct GNUNET_FS_UnindexContext *u; | ||
55 | |||
56 | if (uc != NULL) | ||
57 | { | ||
58 | u = uc; | ||
59 | uc = NULL; | ||
60 | GNUNET_FS_unindex_stop (u); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Called by FS client to give information about the progress of an | ||
67 | * operation. | ||
68 | * | ||
69 | * @param cls closure | ||
70 | * @param info details about the event, specifying the event type | ||
71 | * and various bits about the event | ||
72 | * @return client-context (for the next progress call | ||
73 | * for this operation; should be set to NULL for | ||
74 | * SUSPEND and STOPPED events). The value returned | ||
75 | * will be passed to future callbacks in the respective | ||
76 | * field in the GNUNET_FS_ProgressInfo struct. | ||
77 | */ | ||
78 | static void * | ||
79 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
80 | { | ||
81 | const char *s; | ||
82 | |||
83 | switch (info->status) | ||
84 | { | ||
85 | case GNUNET_FS_STATUS_UNINDEX_START: | ||
86 | break; | ||
87 | |||
88 | case GNUNET_FS_STATUS_UNINDEX_PROGRESS: | ||
89 | if (verbose) | ||
90 | { | ||
91 | s = GNUNET_STRINGS_relative_time_to_string (info->value.unindex.eta, | ||
92 | GNUNET_YES); | ||
93 | fprintf (stdout, | ||
94 | _ ("Unindexing at %llu/%llu (%s remaining)\n"), | ||
95 | (unsigned long long) info->value.unindex.completed, | ||
96 | (unsigned long long) info->value.unindex.size, | ||
97 | s); | ||
98 | } | ||
99 | break; | ||
100 | |||
101 | case GNUNET_FS_STATUS_UNINDEX_ERROR: | ||
102 | fprintf (stderr, | ||
103 | _ ("Error unindexing: %s.\n"), | ||
104 | info->value.unindex.specifics.error.message); | ||
105 | GNUNET_SCHEDULER_shutdown (); | ||
106 | break; | ||
107 | |||
108 | case GNUNET_FS_STATUS_UNINDEX_COMPLETED: | ||
109 | fprintf (stdout, "%s", _ ("Unindexing done.\n")); | ||
110 | GNUNET_SCHEDULER_shutdown (); | ||
111 | break; | ||
112 | |||
113 | case GNUNET_FS_STATUS_UNINDEX_STOPPED: | ||
114 | GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); | ||
115 | break; | ||
116 | |||
117 | default: | ||
118 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
119 | break; | ||
120 | } | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Main function that will be run by the scheduler. | ||
127 | * | ||
128 | * @param cls closure | ||
129 | * @param args remaining command-line arguments | ||
130 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
131 | * @param c configuration | ||
132 | */ | ||
133 | static void | ||
134 | run (void *cls, | ||
135 | char *const *args, | ||
136 | const char *cfgfile, | ||
137 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
138 | { | ||
139 | /* check arguments */ | ||
140 | if ((args[0] == NULL) || (args[1] != NULL)) | ||
141 | { | ||
142 | printf (_ ("You must specify one and only one filename for unindexing.\n")); | ||
143 | ret = -1; | ||
144 | return; | ||
145 | } | ||
146 | cfg = c; | ||
147 | ctx = GNUNET_FS_start (cfg, | ||
148 | "gnunet-unindex", | ||
149 | &progress_cb, | ||
150 | NULL, | ||
151 | GNUNET_FS_FLAGS_NONE, | ||
152 | GNUNET_FS_OPTIONS_END); | ||
153 | if (NULL == ctx) | ||
154 | { | ||
155 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
156 | ret = 1; | ||
157 | return; | ||
158 | } | ||
159 | uc = GNUNET_FS_unindex_start (ctx, args[0], NULL); | ||
160 | if (NULL == uc) | ||
161 | { | ||
162 | fprintf (stderr, "%s", _ ("Could not start unindex operation.\n")); | ||
163 | GNUNET_FS_stop (ctx); | ||
164 | return; | ||
165 | } | ||
166 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
167 | } | ||
168 | |||
169 | |||
170 | /** | ||
171 | * The main function to unindex content. | ||
172 | * | ||
173 | * @param argc number of arguments from the command line | ||
174 | * @param argv command line arguments | ||
175 | * @return 0 ok, 1 on error | ||
176 | */ | ||
177 | int | ||
178 | main (int argc, char *const *argv) | ||
179 | { | ||
180 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
181 | GNUNET_GETOPT_option_verbose (&verbose), | ||
182 | |||
183 | GNUNET_GETOPT_OPTION_END | ||
184 | }; | ||
185 | |||
186 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
187 | return 2; | ||
188 | |||
189 | ret = (GNUNET_OK == | ||
190 | GNUNET_PROGRAM_run ( | ||
191 | argc, | ||
192 | argv, | ||
193 | "gnunet-unindex [OPTIONS] FILENAME", | ||
194 | gettext_noop ( | ||
195 | "Unindex a file that was previously indexed with gnunet-publish."), | ||
196 | options, | ||
197 | &run, | ||
198 | NULL)) | ||
199 | ? ret | ||
200 | : 1; | ||
201 | GNUNET_free_nz ((void *) argv); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* end of gnunet-unindex.c */ | ||
diff --git a/src/cli/fs/meson.build b/src/cli/fs/meson.build new file mode 100644 index 000000000..1b29dd56d --- /dev/null +++ b/src/cli/fs/meson.build | |||
@@ -0,0 +1,51 @@ | |||
1 | executable ('gnunet-search', | ||
2 | 'gnunet-search.c', | ||
3 | dependencies: [libgnunetfs_dep, | ||
4 | libgnunetutil_dep], | ||
5 | include_directories: [incdir, configuration_inc], | ||
6 | install: true, | ||
7 | install_dir: get_option('bindir')) | ||
8 | executable ('gnunet-unindex', | ||
9 | 'gnunet-unindex.c', | ||
10 | dependencies: [libgnunetfs_dep, | ||
11 | libgnunetutil_dep], | ||
12 | include_directories: [incdir, configuration_inc], | ||
13 | install: true, | ||
14 | install_dir: get_option('bindir')) | ||
15 | executable ('gnunet-auto-share', | ||
16 | 'gnunet-auto-share.c', | ||
17 | dependencies: [libgnunetfs_dep, | ||
18 | libgnunetutil_dep], | ||
19 | include_directories: [incdir, configuration_inc], | ||
20 | install: true, | ||
21 | install_dir: get_option('bindir')) | ||
22 | executable ('gnunet-directory', | ||
23 | 'gnunet-directory.c', | ||
24 | dependencies: [libgnunetfs_dep, | ||
25 | libgnunetutil_dep], | ||
26 | include_directories: [incdir, configuration_inc], | ||
27 | install: true, | ||
28 | install_dir: get_option('bindir')) | ||
29 | executable ('gnunet-download', | ||
30 | 'gnunet-download.c', | ||
31 | dependencies: [libgnunetfs_dep, | ||
32 | libgnunetutil_dep], | ||
33 | include_directories: [incdir, configuration_inc], | ||
34 | install: true, | ||
35 | install_dir: get_option('bindir')) | ||
36 | executable ('gnunet-fs', | ||
37 | 'gnunet-fs.c', | ||
38 | dependencies: [libgnunetfs_dep, | ||
39 | libgnunetutil_dep], | ||
40 | include_directories: [incdir, configuration_inc], | ||
41 | install: true, | ||
42 | install_dir: get_option('bindir')) | ||
43 | executable ('gnunet-publish', | ||
44 | 'gnunet-publish.c', | ||
45 | dependencies: [libgnunetfs_dep, | ||
46 | libgnunetidentity_dep, | ||
47 | libgnunetutil_dep], | ||
48 | include_directories: [incdir, configuration_inc], | ||
49 | install: true, | ||
50 | install_dir: get_option('bindir')) | ||
51 | |||
diff --git a/src/cli/gns/.gitignore b/src/cli/gns/.gitignore new file mode 100644 index 000000000..23bd1d13b --- /dev/null +++ b/src/cli/gns/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-gns | ||
2 | gnunet-gns-proxy-setup-ca | ||
diff --git a/src/cli/gns/Makefile.am b/src/cli/gns/Makefile.am new file mode 100644 index 000000000..ae167bca5 --- /dev/null +++ b/src/cli/gns/Makefile.am | |||
@@ -0,0 +1,108 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgdata_DATA = \ | ||
5 | gnunet-gns-proxy-ca.template | ||
6 | |||
7 | if USE_COVERAGE | ||
8 | AM_CFLAGS = --coverage -O0 | ||
9 | endif | ||
10 | |||
11 | if HAVE_LIBIDN | ||
12 | LIBIDN= -lidn | ||
13 | else | ||
14 | LIBIDN= | ||
15 | endif | ||
16 | |||
17 | if HAVE_LIBIDN2 | ||
18 | LIBIDN2= -lidn2 | ||
19 | else | ||
20 | LIBIDN2= | ||
21 | endif | ||
22 | |||
23 | pkgcfgdir = $(pkgdatadir)/config.d/ | ||
24 | |||
25 | libexecdir= $(pkglibdir)/libexec/ | ||
26 | |||
27 | plugindir = $(libdir)/gnunet | ||
28 | |||
29 | bin_PROGRAMS = \ | ||
30 | gnunet-gns | ||
31 | |||
32 | bin_SCRIPTS = \ | ||
33 | gnunet-gns-proxy-setup-ca | ||
34 | |||
35 | gnunet-gns-proxy-setup-ca: gnunet-gns-proxy-setup-ca.in Makefile | ||
36 | $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/gnunet-gns-proxy-setup-ca.in > gnunet-gns-proxy-setup-ca | ||
37 | @chmod +x gnunet-gns-proxy-setup-ca | ||
38 | |||
39 | test_gnunet_gns.sh: test_gnunet_gns.sh.in Makefile | ||
40 | $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/test_gnunet_gns.sh.in > test_gnunet_gns.sh | ||
41 | @chmod +x test_gnunet_gns.sh | ||
42 | |||
43 | CLEANFILES = test_gnunet_gns.sh | ||
44 | |||
45 | gnunet_gns_SOURCES = \ | ||
46 | gnunet-gns.c | ||
47 | gnunet_gns_LDADD = \ | ||
48 | $(top_builddir)/src/service/gns/libgnunetgns.la \ | ||
49 | $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ | ||
50 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
51 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
52 | $(LIBIDN) $(LIBIDN2) \ | ||
53 | $(GN_LIBINTL) | ||
54 | |||
55 | check_SCRIPTS = \ | ||
56 | test_gns_lookup.sh \ | ||
57 | test_gns_config_lookup.sh \ | ||
58 | test_gns_ipv6_lookup.sh\ | ||
59 | test_gns_txt_lookup.sh\ | ||
60 | test_gns_caa_lookup.sh\ | ||
61 | test_gns_mx_lookup.sh \ | ||
62 | test_gns_gns2dns_lookup.sh \ | ||
63 | test_gns_gns2dns_zkey_lookup.sh \ | ||
64 | test_gns_gns2dns_cname_lookup.sh \ | ||
65 | test_gns_dht_lookup.sh\ | ||
66 | test_gns_delegated_lookup.sh \ | ||
67 | test_gns_at_lookup.sh\ | ||
68 | test_gns_zkey_lookup.sh\ | ||
69 | test_gns_rel_expiration.sh\ | ||
70 | test_gns_soa_lookup.sh\ | ||
71 | test_gns_revocation.sh\ | ||
72 | test_gns_redirect_lookup.sh | ||
73 | |||
74 | EXTRA_DIST = \ | ||
75 | test_gns_defaults.conf \ | ||
76 | test_gns_lookup.conf \ | ||
77 | test_gns_simple_lookup.conf \ | ||
78 | openssl.cnf \ | ||
79 | gnunet-gns-proxy-setup-ca.in \ | ||
80 | zonefiles/J7POEUT41A8PBFS7KVVDRF88GBOU4HK8PSU5QKVLVE3R9T91E99G.zkey \ | ||
81 | zonefiles/OEFL7A4VEF1B40QLEMTG5D8G1CN6EN16QUSG5R2DT71GRJN34LSG.zkey \ | ||
82 | zonefiles/test_zonekey \ | ||
83 | test_gns_lookup.sh \ | ||
84 | test_gns_config_lookup.sh \ | ||
85 | test_gns_ipv6_lookup.sh\ | ||
86 | test_gns_txt_lookup.sh\ | ||
87 | test_gns_caa_lookup.sh\ | ||
88 | test_gns_mx_lookup.sh \ | ||
89 | test_gns_gns2dns_lookup.sh \ | ||
90 | test_gns_gns2dns_zkey_lookup.sh \ | ||
91 | test_gns_gns2dns_cname_lookup.sh \ | ||
92 | test_gns_dht_lookup.sh\ | ||
93 | test_gns_delegated_lookup.sh \ | ||
94 | test_gns_at_lookup.sh\ | ||
95 | test_gns_zkey_lookup.sh\ | ||
96 | test_gns_rel_expiration.sh\ | ||
97 | test_gns_soa_lookup.sh\ | ||
98 | test_gns_revocation.sh\ | ||
99 | test_gns_redirect_lookup.sh\ | ||
100 | $(pkgdata_DATA) \ | ||
101 | test_gnunet_gns.sh.in | ||
102 | |||
103 | if ENABLE_TEST_RUN | ||
104 | if HAVE_SQLITE | ||
105 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
106 | TESTS = $(check_SCRIPTS) | ||
107 | endif | ||
108 | endif | ||
diff --git a/src/cli/gns/gnunet-gns-proxy-ca.template b/src/cli/gns/gnunet-gns-proxy-ca.template new file mode 100644 index 000000000..b1a0d16fd --- /dev/null +++ b/src/cli/gns/gnunet-gns-proxy-ca.template | |||
@@ -0,0 +1,303 @@ | |||
1 | # X.509 Certificate options | ||
2 | # | ||
3 | # DN options | ||
4 | |||
5 | # The organization of the subject. | ||
6 | organization = "GNU" | ||
7 | |||
8 | # The organizational unit of the subject. | ||
9 | unit = "GNUnet" | ||
10 | |||
11 | # The locality of the subject. | ||
12 | locality = World | ||
13 | |||
14 | # The state of the certificate owner. | ||
15 | # state = "Attiki" | ||
16 | |||
17 | # The country of the subject. Two letter code. | ||
18 | country = ZZ | ||
19 | |||
20 | # The common name of the certificate owner. | ||
21 | cn = "GNS Proxy CA" | ||
22 | |||
23 | # A user id of the certificate owner. | ||
24 | #uid = "clauper" | ||
25 | |||
26 | # Set domain components | ||
27 | #dc = "name" | ||
28 | #dc = "domain" | ||
29 | |||
30 | # If the supported DN OIDs are not adequate you can set | ||
31 | # any OID here. | ||
32 | # For example set the X.520 Title and the X.520 Pseudonym | ||
33 | # by using OID and string pairs. | ||
34 | #dn_oid = "2.5.4.12 Dr." | ||
35 | #dn_oid = "2.5.4.65 jackal" | ||
36 | |||
37 | # This is deprecated and should not be used in new | ||
38 | # certificates. | ||
39 | # pkcs9_email = "none@none.org" | ||
40 | |||
41 | # An alternative way to set the certificate's distinguished name directly | ||
42 | # is with the "dn" option. The attribute names allowed are: | ||
43 | # C (country), street, O (organization), OU (unit), title, CN (common name), | ||
44 | # L (locality), ST (state), placeOfBirth, gender, countryOfCitizenship, | ||
45 | # countryOfResidence, serialNumber, telephoneNumber, surName, initials, | ||
46 | # generationQualifier, givenName, pseudonym, dnQualifier, postalCode, name, | ||
47 | # businessCategory, DC, UID, jurisdictionOfIncorporationLocalityName, | ||
48 | # jurisdictionOfIncorporationStateOrProvinceName, | ||
49 | # jurisdictionOfIncorporationCountryName, XmppAddr, and numeric OIDs. | ||
50 | |||
51 | #dn = "cn = Nikos,st = New\, Something,C=GR,surName=Mavrogiannopoulos,2.5.4.9=Arkadias" | ||
52 | |||
53 | # The serial number of the certificate | ||
54 | # The value is in decimal (e.g. 1963) or hex (e.g. 0x07ab). | ||
55 | # Comment the field for a random serial number. | ||
56 | #serial = 007 | ||
57 | |||
58 | # In how many days, counting from today, this certificate will expire. | ||
59 | # Use -1 if there is no expiration date. | ||
60 | expiration_days = 3650 | ||
61 | |||
62 | # Alternatively you may set concrete dates and time. The GNU date string | ||
63 | # formats are accepted. See: | ||
64 | # https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html | ||
65 | |||
66 | #activation_date = "2004-02-29 16:21:42" | ||
67 | #expiration_date = "2025-02-29 16:24:41" | ||
68 | |||
69 | # X.509 v3 extensions | ||
70 | |||
71 | # A dnsname in case of a WWW server. | ||
72 | #dns_name = "www.none.org" | ||
73 | #dns_name = "www.morethanone.org" | ||
74 | |||
75 | # An othername defined by an OID and a hex encoded string | ||
76 | #other_name = "1.3.6.1.5.2.2 302ca00d1b0b56414e5245494e2e4f5247a11b3019a006020400000002a10f300d1b047269636b1b0561646d696e" | ||
77 | #other_name_utf8 = "1.2.4.5.6 A UTF8 string" | ||
78 | #other_name_octet = "1.2.4.5.6 A string that will be encoded as ASN.1 octet string" | ||
79 | |||
80 | # Allows writing an XmppAddr Identifier | ||
81 | #xmpp_name = juliet@im.example.com | ||
82 | |||
83 | # Names used in PKINIT | ||
84 | #krb5_principal = user@REALM.COM | ||
85 | #krb5_principal = HTTP/user@REALM.COM | ||
86 | |||
87 | # A subject alternative name URI | ||
88 | #uri = "https://www.example.com" | ||
89 | |||
90 | # An IP address in case of a server. | ||
91 | #ip_address = "192.168.1.1" | ||
92 | |||
93 | # An email in case of a person | ||
94 | email = "bounce@gnunet.org" | ||
95 | |||
96 | # TLS feature (rfc7633) extension. That can is used to indicate mandatory TLS | ||
97 | # extension features to be provided by the server. In practice this is used | ||
98 | # to require the Status Request (extid: 5) extension from the server. That is, | ||
99 | # to require the server holding this certificate to provide a stapled OCSP response. | ||
100 | # You can have multiple lines for multiple TLS features. | ||
101 | |||
102 | # To ask for OCSP status request use: | ||
103 | #tls_feature = 5 | ||
104 | |||
105 | # Challenge password used in certificate requests | ||
106 | challenge_password = 123456 | ||
107 | |||
108 | # Password when encrypting a private key | ||
109 | #password = secret | ||
110 | |||
111 | # An URL that has CRLs (certificate revocation lists) | ||
112 | # available. Needed in CA certificates. | ||
113 | #crl_dist_points = "https://www.getcrl.crl/getcrl/" | ||
114 | |||
115 | # Whether this is a CA certificate or not | ||
116 | ca | ||
117 | |||
118 | # Subject Unique ID (in hex) | ||
119 | #subject_unique_id = 00153224 | ||
120 | |||
121 | # Issuer Unique ID (in hex) | ||
122 | #issuer_unique_id = 00153225 | ||
123 | |||
124 | #### Key usage | ||
125 | |||
126 | # The following key usage flags are used by CAs and end certificates | ||
127 | |||
128 | # Whether this certificate will be used to sign data (needed | ||
129 | # in TLS DHE ciphersuites). This is the digitalSignature flag | ||
130 | # in RFC5280 terminology. | ||
131 | signing_key | ||
132 | |||
133 | # Whether this certificate will be used to encrypt data (needed | ||
134 | # in TLS RSA ciphersuites). Note that it is preferred to use different | ||
135 | # keys for encryption and signing. This is the keyEncipherment flag | ||
136 | # in RFC5280 terminology. | ||
137 | encryption_key | ||
138 | |||
139 | # Whether this key will be used to sign other certificates. The | ||
140 | # keyCertSign flag in RFC5280 terminology. | ||
141 | cert_signing_key | ||
142 | |||
143 | # Whether this key will be used to sign CRLs. The | ||
144 | # cRLSign flag in RFC5280 terminology. | ||
145 | #crl_signing_key | ||
146 | |||
147 | # The keyAgreement flag of RFC5280. It's purpose is loosely | ||
148 | # defined. Not use it unless required by a protocol. | ||
149 | #key_agreement | ||
150 | |||
151 | # The dataEncipherment flag of RFC5280. It's purpose is loosely | ||
152 | # defined. Not use it unless required by a protocol. | ||
153 | #data_encipherment | ||
154 | |||
155 | # The nonRepudiation flag of RFC5280. It's purpose is loosely | ||
156 | # defined. Not use it unless required by a protocol. | ||
157 | #non_repudiation | ||
158 | |||
159 | #### Extended key usage (key purposes) | ||
160 | |||
161 | # The following extensions are used in an end certificate | ||
162 | # to clarify its purpose. Some CAs also use it to indicate | ||
163 | # the types of certificates they are purposed to sign. | ||
164 | |||
165 | |||
166 | # Whether this certificate will be used for a TLS client; | ||
167 | # this sets the id-kp-clientAuth (1.3.6.1.5.5.7.3.2) of | ||
168 | # extended key usage. | ||
169 | #tls_www_client | ||
170 | |||
171 | # Whether this certificate will be used for a TLS server; | ||
172 | # this sets the id-kp-serverAuth (1.3.6.1.5.5.7.3.1) of | ||
173 | # extended key usage. | ||
174 | tls_www_server | ||
175 | |||
176 | # Whether this key will be used to sign code. This sets the | ||
177 | # id-kp-codeSigning (1.3.6.1.5.5.7.3.3) of extended key usage | ||
178 | # extension. | ||
179 | #code_signing_key | ||
180 | |||
181 | # Whether this key will be used to sign OCSP data. This sets the | ||
182 | # id-kp-OCSPSigning (1.3.6.1.5.5.7.3.9) of extended key usage extension. | ||
183 | #ocsp_signing_key | ||
184 | |||
185 | # Whether this key will be used for time stamping. This sets the | ||
186 | # id-kp-timeStamping (1.3.6.1.5.5.7.3.8) of extended key usage extension. | ||
187 | #time_stamping_key | ||
188 | |||
189 | # Whether this key will be used for email protection. This sets the | ||
190 | # id-kp-emailProtection (1.3.6.1.5.5.7.3.4) of extended key usage extension. | ||
191 | #email_protection_key | ||
192 | |||
193 | # Whether this key will be used for IPsec IKE operations (1.3.6.1.5.5.7.3.17). | ||
194 | #ipsec_ike_key | ||
195 | |||
196 | ## adding custom key purpose OIDs | ||
197 | |||
198 | # for microsoft smart card logon | ||
199 | # key_purpose_oid = 1.3.6.1.4.1.311.20.2.2 | ||
200 | |||
201 | # for email protection | ||
202 | # key_purpose_oid = 1.3.6.1.5.5.7.3.4 | ||
203 | |||
204 | # for any purpose (must not be used in intermediate CA certificates) | ||
205 | # key_purpose_oid = 2.5.29.37.0 | ||
206 | |||
207 | ### end of key purpose OIDs | ||
208 | |||
209 | ### Adding arbitrary extensions | ||
210 | # This requires to provide the extension OIDs, as well as the extension data in | ||
211 | # hex format. The following two options are available since GnuTLS 3.5.3. | ||
212 | #add_extension = "1.2.3.4 0x0AAB01ACFE" | ||
213 | |||
214 | # As above but encode the data as an octet string | ||
215 | #add_extension = "1.2.3.4 octet_string(0x0AAB01ACFE)" | ||
216 | |||
217 | # For portability critical extensions shouldn't be set to certificates. | ||
218 | #add_critical_extension = "5.6.7.8 0x1AAB01ACFE" | ||
219 | |||
220 | # When generating a certificate from a certificate | ||
221 | # request, then honor the extensions stored in the request | ||
222 | # and store them in the real certificate. | ||
223 | #honor_crq_extensions | ||
224 | |||
225 | # Alternatively only specific extensions can be copied. | ||
226 | #honor_crq_ext = 2.5.29.17 | ||
227 | #honor_crq_ext = 2.5.29.15 | ||
228 | |||
229 | # Path length constraint. Sets the maximum number of | ||
230 | # certificates that can be used to certify this certificate. | ||
231 | # (i.e. the certificate chain length) | ||
232 | #path_len = -1 | ||
233 | #path_len = 2 | ||
234 | |||
235 | # OCSP URI | ||
236 | # ocsp_uri = https://my.ocsp.server/ocsp | ||
237 | |||
238 | # CA issuers URI | ||
239 | # ca_issuers_uri = https://my.ca.issuer | ||
240 | |||
241 | # Certificate policies | ||
242 | #policy1 = 1.3.6.1.4.1.5484.1.10.99.1.0 | ||
243 | #policy1_txt = "This is a long policy to summarize" | ||
244 | #policy1_url = https://www.example.com/a-policy-to-read | ||
245 | |||
246 | #policy2 = 1.3.6.1.4.1.5484.1.10.99.1.1 | ||
247 | #policy2_txt = "This is a short policy" | ||
248 | #policy2_url = https://www.example.com/another-policy-to-read | ||
249 | |||
250 | # The number of additional certificates that may appear in a | ||
251 | # path before the anyPolicy is no longer acceptable. | ||
252 | #inhibit_anypolicy_skip_certs 1 | ||
253 | |||
254 | # Name constraints | ||
255 | |||
256 | # DNS | ||
257 | #nc_permit_dns = example.com | ||
258 | #nc_exclude_dns = test.example.com | ||
259 | |||
260 | |||
261 | #nc_permit_email = "nmav@ex.net" | ||
262 | |||
263 | # Exclude subdomains of example.com | ||
264 | #nc_exclude_email = .example.com | ||
265 | |||
266 | # Exclude all e-mail addresses of example.com | ||
267 | #nc_exclude_email = example.com | ||
268 | |||
269 | # IP | ||
270 | #nc_permit_ip = 192.168.0.0/16 | ||
271 | #nc_exclude_ip = 192.168.5.0/24 | ||
272 | #nc_permit_ip = fc0a:eef2:e7e7:a56e::/64 | ||
273 | |||
274 | |||
275 | # Options for proxy certificates | ||
276 | #proxy_policy_language = 1.3.6.1.5.5.7.21.1 | ||
277 | |||
278 | |||
279 | # Options for generating a CRL | ||
280 | |||
281 | # The number of days the next CRL update will be due. | ||
282 | # next CRL update will be in 43 days | ||
283 | #crl_next_update = 43 | ||
284 | |||
285 | # this is the 5th CRL by this CA | ||
286 | # The value is in decimal (e.g. 1963) or hex (e.g. 0x07ab). | ||
287 | # Comment the field for a time-based number. | ||
288 | # Time-based CRL numbers generated in GnuTLS 3.6.3 and later | ||
289 | # are significantly larger than those generated in previous | ||
290 | # versions. Since CRL numbers need to be monotonic, you need | ||
291 | # to specify the CRL number here manually if you intend to | ||
292 | # downgrade to an earlier version than 3.6.3 after publishing | ||
293 | # the CRL as it is not possible to specify CRL numbers greater | ||
294 | # than 2**63-2 using hex notation in those versions. | ||
295 | #crl_number = 5 | ||
296 | |||
297 | # Specify the update dates more precisely. | ||
298 | #crl_this_update_date = "2004-02-29 16:21:42" | ||
299 | #crl_next_update_date = "2025-02-29 16:24:41" | ||
300 | |||
301 | # The date that the certificates will be made seen as | ||
302 | # being revoked. | ||
303 | #crl_revocation_date = "2025-02-29 16:24:41" | ||
diff --git a/src/cli/gns/gnunet-gns-proxy-setup-ca.in b/src/cli/gns/gnunet-gns-proxy-setup-ca.in new file mode 100644 index 000000000..b3ebfd11d --- /dev/null +++ b/src/cli/gns/gnunet-gns-proxy-setup-ca.in | |||
@@ -0,0 +1,339 @@ | |||
1 | #!/bin/sh | ||
2 | # | ||
3 | # This shell script will generate an X509 certificate for | ||
4 | # your gnunet-gns-proxy and install it (for both GNUnet | ||
5 | # and your browser). | ||
6 | # | ||
7 | # TODO: Implement support for more browsers | ||
8 | # TODO: Debug and switch to the new version | ||
9 | # TODO - The only remaining task is fixing the getopts | ||
10 | # TODO: Error checks | ||
11 | # | ||
12 | # The current version partially reuses and recycles | ||
13 | # code from build.sh by NetBSD (although not entirely | ||
14 | # used because it needs debugging): | ||
15 | # | ||
16 | # Copyright (c) 2001-2011 The NetBSD Foundation, Inc. | ||
17 | # All rights reserved. | ||
18 | # | ||
19 | # This code is derived from software contributed to | ||
20 | # The NetBSD Foundation by Todd Vierling and Luke Mewburn. | ||
21 | # | ||
22 | # Redistribution and use in source and binary forms, with or | ||
23 | # without modification, are permitted provided that the following | ||
24 | # conditions are met: | ||
25 | # 1. Redistributions of source code must retain the above | ||
26 | # copyright notice, this list of conditions and the following | ||
27 | # disclaimer. | ||
28 | # 2. Redistributions in binary form must reproduce the above | ||
29 | # copyright notice, this list of conditions and the following | ||
30 | # disclaimer in the documentation and/or other materials | ||
31 | # provided with the distribution. | ||
32 | # | ||
33 | # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND | ||
34 | # CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
35 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
36 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
37 | # DISCLAIMED. | ||
38 | # IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR | ||
39 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
40 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
41 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
42 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
43 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
44 | # LIABILITY, OR TORT | ||
45 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | ||
46 | # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | ||
47 | # OF SUCH DAMAGE. | ||
48 | |||
49 | dir=$(dirname "$0") | ||
50 | |||
51 | progname=${0##*/} | ||
52 | |||
53 | existence() { | ||
54 | command -v "$1" >/dev/null 2>&1 | ||
55 | } | ||
56 | |||
57 | statusmsg() | ||
58 | { | ||
59 | ${runcmd} echo "${tab}$@" | tee -a "${results}" | ||
60 | } | ||
61 | |||
62 | infomsg() | ||
63 | { | ||
64 | if [ x$verbosity = x1 ]; then | ||
65 | statusmsg "INFO:${tab}$@" | ||
66 | fi | ||
67 | } | ||
68 | |||
69 | warningmsg() | ||
70 | { | ||
71 | statusmsg "WARNING:${tab}$@" | ||
72 | } | ||
73 | |||
74 | errormsg() | ||
75 | { | ||
76 | statusmsg "ERROR:${tab}$@" | ||
77 | } | ||
78 | |||
79 | linemsg() | ||
80 | { | ||
81 | statusmsg "=========================================" | ||
82 | } | ||
83 | |||
84 | |||
85 | print_version() | ||
86 | { | ||
87 | GNUNET_ARM_VERSION=`gnunet-arm -v | awk '{print $2 " " $3}'` | ||
88 | echo ${progname} $GNUNET_ARM_VERSION | ||
89 | } | ||
90 | |||
91 | # Whitespace normalization without depending on shell features: | ||
92 | tab=' ' | ||
93 | tab2=' ' | ||
94 | nl=' | ||
95 | ' | ||
96 | |||
97 | setdefaults() | ||
98 | { | ||
99 | verbosity=0 | ||
100 | resfile= | ||
101 | results=/dev/null | ||
102 | tmpdir=${TMPDIR:-/tmp} | ||
103 | runcmd= | ||
104 | } | ||
105 | |||
106 | usage() | ||
107 | { | ||
108 | if [ -n "$*" ]; then | ||
109 | echo "${nl}${progname}: $*" | ||
110 | fi | ||
111 | cat <<_usage_ | ||
112 | |||
113 | Usage: ${progname} [-hvVto] [-c FILE] | ||
114 | |||
115 | Options: | ||
116 | ${tab}-c FILE Use the configuration file FILE. | ||
117 | ${tab}-h${tab2}${tab2}Print this help message. | ||
118 | ${tab}-o${tab2}${tab2}Display summary of statusmessages | ||
119 | ${tab}-t${tab2}${tab2}Short developer test on binaries | ||
120 | ${tab}-v${tab2}${tab2}Print the version and exit. | ||
121 | ${tab}-V${tab2}${tab2}be verbose | ||
122 | |||
123 | _usage_ | ||
124 | exit 1 | ||
125 | } | ||
126 | |||
127 | |||
128 | generate_ca() | ||
129 | { | ||
130 | echo "" | ||
131 | infomsg "Generating CA" | ||
132 | TMPDIR=${TMPDIR:-/tmp} | ||
133 | if test -e "$TMPDIR"; then | ||
134 | GNSCERT=`mktemp -t cert.pem.XXXXXXXX` || exit 1 | ||
135 | GNSCAKY=`mktemp -t caky.pem.XXXXXXXX` || exit 1 | ||
136 | GNSCANO=`mktemp -t cano.pem.XXXXXXXX` || exit 1 | ||
137 | else | ||
138 | # This warning is mostly pointless. | ||
139 | warningmsg "You need to export the TMPDIR variable" | ||
140 | fi | ||
141 | |||
142 | # # ------------- gnutls | ||
143 | # | ||
144 | # if ! which certutil > /dev/null | ||
145 | # then | ||
146 | # warningmsg "The 'certutil' command was not found." | ||
147 | # warningmsg "Not importing into browsers." | ||
148 | # warningmsg "For 'certutil' install nss." | ||
149 | # else | ||
150 | # # Generate CA key | ||
151 | # # pkcs#8 password-protects key | ||
152 | # certtool --pkcs8 --generate-privkey --sec-param high --outfile ca-key.pem | ||
153 | # # self-sign the CA to create public certificate | ||
154 | # certtool --generate-self-signed --load-privkey ca-key.pem --template ca.cfg --outfile ca.pem | ||
155 | |||
156 | # ------------- openssl | ||
157 | |||
158 | GNUTLS_CA_TEMPLATE=@PKGDATADIRECTORY@/gnunet-gns-proxy-ca.template | ||
159 | OPENSSLCFG=@PKGDATADIRECTORY@/openssl.cnf | ||
160 | CERTTOOL="" | ||
161 | OPENSSL=0 | ||
162 | if test -x $(existence gnunet-certtool) | ||
163 | # if test -z "`gnutls-certtool --version`" > /dev/null | ||
164 | then | ||
165 | # We only support gnutls certtool for now. Treat the grep | ||
166 | # for "gnutls" in the output with extra care, it only matches | ||
167 | # the email address! It is probably safer to run strings(1) | ||
168 | # over certtool for a string matching "gnutls" | ||
169 | if test -z "`certtool --version | grep gnutls`" > /dev/null | ||
170 | then | ||
171 | warningmsg "'gnutls-certtool' or 'certtool' command not found. Trying openssl." | ||
172 | # if test -z "`openssl version`" > /dev/null | ||
173 | if test -x $(existence openssl) | ||
174 | then | ||
175 | OPENSSL=1 | ||
176 | else | ||
177 | warningmsg "Install either gnutls certtool or openssl for certificate generation!" | ||
178 | statusmsg "Cleaning up." | ||
179 | rm -f $GNSCAKY $GNSCERT | ||
180 | exit 1 | ||
181 | fi | ||
182 | fi | ||
183 | CERTTOOL="certtool" | ||
184 | else | ||
185 | CERTTOOL="gnutls-certtool" | ||
186 | fi | ||
187 | if test -n "${GNUNET_CONFIG_FILE}"; then | ||
188 | GNUNET_CONFIG="-c ${GNUNET_CONFIG_FILE}" | ||
189 | else | ||
190 | GNUNET_CONFIG="" | ||
191 | fi | ||
192 | GNS_CA_CERT_PEM=`gnunet-config ${GNUNET_CONFIG} -s gns-proxy -o PROXY_CACERT -f ${options}` | ||
193 | mkdir -p `dirname $GNS_CA_CERT_PEM` | ||
194 | |||
195 | if test 1 -eq $OPENSSL | ||
196 | then | ||
197 | if test 1 -eq $verbosity; then | ||
198 | openssl req -config $OPENSSLCFG -new -x509 -days 3650 -extensions v3_ca -keyout $GNSCAKY -out $GNSCERT -subj "/C=ZZ/L=World/O=GNU/OU=GNUnet/CN=GNS Proxy CA/emailAddress=bounce@gnunet.org" -passout pass:"GNU Name System" | ||
199 | else | ||
200 | openssl req -config $OPENSSLCFG -new -x509 -days 3650 -extensions v3_ca -keyout $GNSCAKY -out $GNSCERT -subj "/C=ZZ/L=World/O=GNU/OU=GNUnet/CN=GNS Proxy CA/emailAddress=bounce@gnunet.org" -passout pass:"GNU Name System" >/dev/null 2>&1 | ||
201 | fi | ||
202 | infomsg "Removing passphrase from key" | ||
203 | if test 1 -eq $verbosity; then | ||
204 | openssl rsa -passin pass:"GNU Name System" -in $GNSCAKY -out $GNSCANO | ||
205 | else | ||
206 | openssl rsa -passin pass:"GNU Name System" -in $GNSCAKY -out $GNSCANO >/dev/null 2>&1 | ||
207 | fi | ||
208 | cat $GNSCERT $GNSCANO > $GNS_CA_CERT_PEM | ||
209 | else | ||
210 | if test 1 -eq $verbosity; then | ||
211 | $CERTTOOL --generate-privkey --outfile $GNSCAKY | ||
212 | $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $GNSCAKY --outfile $GNSCERT | ||
213 | else | ||
214 | $CERTTOOL --generate-privkey --outfile $GNSCAKY >/dev/null 2>&1 | ||
215 | $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $GNSCAKY --outfile $GNSCERT >/dev/null 2>&1 | ||
216 | fi | ||
217 | infomsg "Making private key available to gnunet-gns-proxy" | ||
218 | cat $GNSCERT $GNSCAKY > $GNS_CA_CERT_PEM | ||
219 | fi | ||
220 | } | ||
221 | |||
222 | importbrowsers() | ||
223 | { | ||
224 | # if test -z "`command -v certutil`" > /dev/null 2>&1 | ||
225 | if test -x $(existence gnutls-certutil) || test -x $(existence certutil) | ||
226 | then | ||
227 | statusmsg "Importing CA into browsers" | ||
228 | # TODO: Error handling? | ||
229 | for f in ~/.mozilla/firefox/*.*/ | ||
230 | do | ||
231 | if [ -d $f ]; then | ||
232 | infomsg "Importing CA into Firefox at $f" | ||
233 | # delete old certificate (if any) | ||
234 | certutil -D -n "GNS Proxy CA" -d "$f" >/dev/null 2>/dev/null | ||
235 | # add new certificate | ||
236 | certutil -A -n "GNS Proxy CA" -t CT,, -d "$f" < $GNSCERT | ||
237 | fi | ||
238 | done | ||
239 | for f in ~/.mozilla/icecat/*.*/ | ||
240 | do | ||
241 | if [ -d $f ]; then | ||
242 | infomsg "Importing CA into Icecat at $f" | ||
243 | # delete old certificate (if any) | ||
244 | certutil -D -n "GNS Proxy CA" -d "$f" >/dev/null 2>/dev/null | ||
245 | # add new certificate | ||
246 | certutil -A -n "GNS Proxy CA" -t CT,, -d "$f" < $GNSCERT | ||
247 | fi | ||
248 | done | ||
249 | # TODO: Error handling? | ||
250 | if [ -d ~/.pki/nssdb/ ]; then | ||
251 | statusmsg "Importing CA into Chrome at ~/.pki/nssdb/" | ||
252 | # delete old certificate (if any) | ||
253 | certutil -D -n "GNS Proxy CA" -d ~/.pki/nssdb/ >/dev/null 2>/dev/null | ||
254 | # add new certificate | ||
255 | certutil -A -n "GNS Proxy CA" -t CT,, -d ~/.pki/nssdb/ < $GNSCERT | ||
256 | fi | ||
257 | else | ||
258 | warningmsg "The 'certutil' command was not found." | ||
259 | warningmsg "Not importing into browsers." | ||
260 | warningmsg "For 'certutil' install nss." | ||
261 | fi | ||
262 | } | ||
263 | |||
264 | clean_up() | ||
265 | { | ||
266 | infomsg "Cleaning up." | ||
267 | rm -f $GNSCAKY $GNSCANO $GNSCERT | ||
268 | if test -e $SETUP_TMPDIR | ||
269 | then | ||
270 | rm -rf $SETUP_TMPDIR | ||
271 | fi | ||
272 | |||
273 | linemsg | ||
274 | statusmsg "You can now start gnunet-gns-proxy." | ||
275 | statusmsg "Afterwards, configure your browser " | ||
276 | statusmsg "to use a SOCKS proxy on port 7777. " | ||
277 | linemsg | ||
278 | } | ||
279 | |||
280 | main() | ||
281 | { | ||
282 | setdefaults | ||
283 | while getopts "vhVtoc:" opt; do | ||
284 | case $opt in | ||
285 | v) | ||
286 | print_version | ||
287 | exit 0 | ||
288 | ;; | ||
289 | h) | ||
290 | usage | ||
291 | ;; | ||
292 | V) | ||
293 | verbosity=1 | ||
294 | ;; | ||
295 | c) | ||
296 | options="$options -c $OPTARG" | ||
297 | infomsg "Using configuration file $OPTARG" | ||
298 | GNUNET_CONFIG_FILE=${OPTARG} | ||
299 | ;; | ||
300 | t) | ||
301 | verbosity=1 | ||
302 | infomsg "Running short developer test" | ||
303 | if test -x $(existence openssl); then | ||
304 | openssl version | ||
305 | fi | ||
306 | if test -x $(existence certtool); then | ||
307 | certtool --version | ||
308 | fi | ||
309 | if test -x $(existence gnutls-certtool); then | ||
310 | gnutls-certtool --version | ||
311 | fi | ||
312 | exit 0 | ||
313 | ;; | ||
314 | o) | ||
315 | resfile=$(mktemp -t ${progname}.results) | ||
316 | results="${resfile}" | ||
317 | ;; | ||
318 | \?) | ||
319 | echo "Invalid option: -$OPTARG" >&2 | ||
320 | usage | ||
321 | ;; | ||
322 | :) | ||
323 | echo "Option -$OPTARG requires an argument." >&2 | ||
324 | usage | ||
325 | ;; | ||
326 | esac | ||
327 | done | ||
328 | generate_ca | ||
329 | importbrowsers | ||
330 | if [ -s "${results}" ]; then | ||
331 | echo "===> Summary of results:" | ||
332 | sed -e 's/^===>//;s/^/ /' "${results}" | ||
333 | echo "===> ." | ||
334 | infomsg "Please remove ${results} manually." | ||
335 | fi | ||
336 | clean_up | ||
337 | } | ||
338 | |||
339 | main "$@" | ||
diff --git a/src/cli/gns/gnunet-gns.c b/src/cli/gns/gnunet-gns.c new file mode 100644 index 000000000..724fbce24 --- /dev/null +++ b/src/cli/gns/gnunet-gns.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2013, 2017-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 gnunet-gns.c | ||
22 | * @brief command line tool to access distributed GNS | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #if HAVE_LIBIDN2 | ||
27 | #if HAVE_IDN2_H | ||
28 | #include <idn2.h> | ||
29 | #elif HAVE_IDN2_IDN2_H | ||
30 | #include <idn2/idn2.h> | ||
31 | #endif | ||
32 | #elif HAVE_LIBIDN | ||
33 | #if HAVE_IDNA_H | ||
34 | #include <idna.h> | ||
35 | #elif HAVE_IDN_IDNA_H | ||
36 | #include <idn/idna.h> | ||
37 | #endif | ||
38 | #endif | ||
39 | #include <gnunet_util_lib.h> | ||
40 | #include <gnunet_gnsrecord_lib.h> | ||
41 | #include <gnunet_namestore_service.h> | ||
42 | #include <gnunet_gns_service.h> | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Configuration we are using. | ||
47 | */ | ||
48 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
49 | |||
50 | /** | ||
51 | * Handle to GNS service. | ||
52 | */ | ||
53 | static struct GNUNET_GNS_Handle *gns; | ||
54 | |||
55 | /** | ||
56 | * GNS name to lookup. (-u option) | ||
57 | */ | ||
58 | static char *lookup_name; | ||
59 | |||
60 | /** | ||
61 | * DNS IDNA name to lookup. (set if -d option is set) | ||
62 | */ | ||
63 | char *idna_name; | ||
64 | |||
65 | /** | ||
66 | * DNS compatibility (name is given as DNS name, possible IDNA). | ||
67 | */ | ||
68 | static int dns_compat; | ||
69 | |||
70 | /** | ||
71 | * record type to look up (-t option) | ||
72 | */ | ||
73 | static char *lookup_type; | ||
74 | |||
75 | /** | ||
76 | * raw output | ||
77 | */ | ||
78 | static int raw; | ||
79 | |||
80 | /** | ||
81 | * Desired record type. | ||
82 | */ | ||
83 | static uint32_t rtype; | ||
84 | |||
85 | /** | ||
86 | * Timeout for lookup | ||
87 | */ | ||
88 | static struct GNUNET_TIME_Relative timeout; | ||
89 | |||
90 | /** | ||
91 | * Timeout task | ||
92 | */ | ||
93 | static struct GNUNET_SCHEDULER_Task *to_task; | ||
94 | |||
95 | /** | ||
96 | * Handle to lookup request | ||
97 | */ | ||
98 | static struct GNUNET_GNS_LookupWithTldRequest *lr; | ||
99 | |||
100 | /** | ||
101 | * Global return value. | ||
102 | * 0 on success (default), | ||
103 | * 1 on internal failures | ||
104 | * 2 on launch failure, | ||
105 | * 4 if the name is not a GNS-supported TLD, | ||
106 | */ | ||
107 | static int global_ret; | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Task run on shutdown. Cleans up everything. | ||
112 | * | ||
113 | * @param cls unused | ||
114 | */ | ||
115 | static void | ||
116 | do_shutdown (void *cls) | ||
117 | { | ||
118 | (void) cls; | ||
119 | if (NULL != to_task) | ||
120 | { | ||
121 | GNUNET_SCHEDULER_cancel (to_task); | ||
122 | to_task = NULL; | ||
123 | } | ||
124 | if (NULL != lr) | ||
125 | { | ||
126 | GNUNET_GNS_lookup_with_tld_cancel (lr); | ||
127 | lr = NULL; | ||
128 | } | ||
129 | if (NULL != gns) | ||
130 | { | ||
131 | GNUNET_GNS_disconnect (gns); | ||
132 | gns = NULL; | ||
133 | } | ||
134 | if (NULL != idna_name) | ||
135 | { | ||
136 | GNUNET_free (idna_name); | ||
137 | idna_name = NULL; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Task to run on timeout | ||
144 | * | ||
145 | * @param cls unused | ||
146 | */ | ||
147 | static void | ||
148 | do_timeout (void*cls) | ||
149 | { | ||
150 | to_task = NULL; | ||
151 | global_ret = 3; // Timeout | ||
152 | GNUNET_SCHEDULER_shutdown (); | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Function called with the result of a GNS lookup. | ||
158 | * | ||
159 | * @param cls the 'const char *' name that was resolved | ||
160 | * @param was_gns #GNUNET_NO if TLD did not indicate use of GNS | ||
161 | * @param rd_count number of records returned | ||
162 | * @param rd array of @a rd_count records with the results | ||
163 | */ | ||
164 | static void | ||
165 | process_lookup_result (void *cls, | ||
166 | int was_gns, | ||
167 | uint32_t rd_count, | ||
168 | const struct GNUNET_GNSRECORD_Data *rd) | ||
169 | { | ||
170 | struct GNUNET_TIME_Relative block_exp; | ||
171 | const char *typename; | ||
172 | char *string_val; | ||
173 | |||
174 | lr = NULL; | ||
175 | if (GNUNET_NO == was_gns) | ||
176 | { | ||
177 | global_ret = 4; /* not for GNS */ | ||
178 | GNUNET_SCHEDULER_shutdown (); | ||
179 | return; | ||
180 | } | ||
181 | block_exp = GNUNET_TIME_absolute_get_remaining ( | ||
182 | GNUNET_GNSRECORD_record_get_expiration_time ( | ||
183 | rd_count, | ||
184 | rd, | ||
185 | GNUNET_TIME_UNIT_ZERO_ABS)); | ||
186 | if (! raw) | ||
187 | { | ||
188 | printf ("<<< %u record(s) found:\n\n", rd_count); | ||
189 | } | ||
190 | for (uint32_t i = 0; i < rd_count; i++) | ||
191 | { | ||
192 | typename = GNUNET_GNSRECORD_number_to_typename (rd[i]. | ||
193 | record_type); | ||
194 | string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type | ||
195 | , | ||
196 | rd[i].data, | ||
197 | rd[i].data_size); | ||
198 | if (NULL == string_val) | ||
199 | { | ||
200 | fprintf (stderr, | ||
201 | "Record %u of type %d malformed, skipping\n", | ||
202 | (unsigned int) i, | ||
203 | (int) rd[i].record_type); | ||
204 | continue; | ||
205 | } | ||
206 | if (raw) | ||
207 | printf ("%s\n", string_val); | ||
208 | else | ||
209 | printf ("%s: `%s' %s\n", | ||
210 | typename, | ||
211 | string_val, | ||
212 | (0 != (rd[i].flags | ||
213 | & | ||
214 | GNUNET_GNSRECORD_RF_SUPPLEMENTAL) | ||
215 | ) ? | ||
216 | "(supplemental)" : ""); | ||
217 | GNUNET_free (string_val); | ||
218 | } | ||
219 | if (! raw) | ||
220 | { | ||
221 | if (0 != rd_count) | ||
222 | printf ("\nRecord set expires in %s.\n", | ||
223 | GNUNET_STRINGS_relative_time_to_string ( | ||
224 | block_exp, GNUNET_YES)); | ||
225 | } | ||
226 | GNUNET_SCHEDULER_shutdown (); | ||
227 | } | ||
228 | |||
229 | |||
230 | /** | ||
231 | * Main function that will be run. | ||
232 | * | ||
233 | * @param cls closure | ||
234 | * @param args remaining command-line arguments | ||
235 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
236 | * @param c configuration | ||
237 | */ | ||
238 | static void | ||
239 | run (void *cls, | ||
240 | char *const *args, | ||
241 | const char *cfgfile, | ||
242 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
243 | { | ||
244 | const char *effective_lookup_type; | ||
245 | (void) cls; | ||
246 | (void) args; | ||
247 | (void) cfgfile; | ||
248 | |||
249 | cfg = c; | ||
250 | to_task = NULL; | ||
251 | { | ||
252 | char *colon; | ||
253 | |||
254 | if (NULL != (colon = strchr (lookup_name, ':'))) | ||
255 | *colon = '\0'; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * If DNS compatibility is requested, we first verify that the | ||
260 | * lookup_name is in a DNS format. If yes, we convert it to UTF-8. | ||
261 | */ | ||
262 | if (GNUNET_YES == dns_compat) | ||
263 | { | ||
264 | Idna_rc rc; | ||
265 | |||
266 | if (GNUNET_OK != GNUNET_DNSPARSER_check_name (lookup_name)) | ||
267 | { | ||
268 | fprintf (stderr, | ||
269 | _ ("`%s' is not a valid DNS domain name\n"), | ||
270 | lookup_name); | ||
271 | global_ret = 3; | ||
272 | return; | ||
273 | } | ||
274 | if (IDNA_SUCCESS != | ||
275 | (rc = idna_to_unicode_8z8z (lookup_name, &idna_name, | ||
276 | IDNA_ALLOW_UNASSIGNED))) | ||
277 | { | ||
278 | fprintf (stderr, | ||
279 | _ ( | ||
280 | "Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"), | ||
281 | lookup_name, | ||
282 | idna_strerror (rc)); | ||
283 | global_ret = 4; | ||
284 | return; | ||
285 | } | ||
286 | lookup_name = idna_name; | ||
287 | } | ||
288 | |||
289 | if (GNUNET_YES != | ||
290 | GNUNET_CLIENT_test (cfg, | ||
291 | "arm")) | ||
292 | { | ||
293 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
294 | _ ( | ||
295 | "Cannot resolve using GNS: GNUnet peer not running\n")); | ||
296 | global_ret = 5; | ||
297 | return; | ||
298 | } | ||
299 | to_task = GNUNET_SCHEDULER_add_delayed (timeout, | ||
300 | &do_timeout, | ||
301 | NULL); | ||
302 | gns = GNUNET_GNS_connect (cfg); | ||
303 | if (NULL == gns) | ||
304 | { | ||
305 | fprintf (stderr, | ||
306 | _ ("Failed to connect to GNS\n")); | ||
307 | global_ret = 2; | ||
308 | return; | ||
309 | } | ||
310 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
311 | NULL); | ||
312 | if (NULL != lookup_type) | ||
313 | { | ||
314 | effective_lookup_type = lookup_type; | ||
315 | rtype = GNUNET_GNSRECORD_typename_to_number (lookup_type); | ||
316 | } | ||
317 | else | ||
318 | { | ||
319 | effective_lookup_type = "A"; | ||
320 | rtype = GNUNET_DNSPARSER_TYPE_A; | ||
321 | } | ||
322 | if (UINT32_MAX == rtype) | ||
323 | { | ||
324 | fprintf (stderr, | ||
325 | _ ("Invalid typename specified, assuming `ANY'\n")); | ||
326 | rtype = GNUNET_GNSRECORD_TYPE_ANY; | ||
327 | } | ||
328 | if (! raw) | ||
329 | { | ||
330 | printf (">>> Looking for `%s' records under `%s'\n", | ||
331 | effective_lookup_type, lookup_name); | ||
332 | } | ||
333 | lr = GNUNET_GNS_lookup_with_tld (gns, | ||
334 | lookup_name, | ||
335 | rtype, | ||
336 | GNUNET_GNS_LO_DEFAULT, | ||
337 | &process_lookup_result, | ||
338 | lookup_name); | ||
339 | if (NULL == lr) | ||
340 | { | ||
341 | global_ret = 2; | ||
342 | GNUNET_SCHEDULER_shutdown (); | ||
343 | return; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | |||
348 | /** | ||
349 | * The main function for gnunet-gns. | ||
350 | * | ||
351 | * @param argc number of arguments from the command line | ||
352 | * @param argv command line arguments | ||
353 | * @return 0 ok, 1 on error | ||
354 | */ | ||
355 | int | ||
356 | main (int argc, char *const *argv) | ||
357 | { | ||
358 | timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
359 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
360 | { GNUNET_GETOPT_option_mandatory ( | ||
361 | GNUNET_GETOPT_option_string ('u', | ||
362 | "lookup", | ||
363 | "NAME", | ||
364 | gettext_noop ( | ||
365 | "Lookup a record for the given name"), | ||
366 | &lookup_name)), | ||
367 | GNUNET_GETOPT_option_string ('t', | ||
368 | "type", | ||
369 | "TYPE", | ||
370 | gettext_noop ( | ||
371 | "Specify the type of the record to lookup"), | ||
372 | &lookup_type), | ||
373 | GNUNET_GETOPT_option_relative_time ('T', | ||
374 | "timeout", | ||
375 | "TIMEOUT", | ||
376 | gettext_noop ( | ||
377 | "Specify a timeout for the lookup"), | ||
378 | &timeout), | ||
379 | GNUNET_GETOPT_option_flag ('r', | ||
380 | "raw", | ||
381 | gettext_noop ("No unneeded output"), | ||
382 | &raw), | ||
383 | GNUNET_GETOPT_option_flag ('d', | ||
384 | "dns", | ||
385 | gettext_noop ( | ||
386 | "DNS Compatibility: Name is passed in IDNA instead of UTF-8"), | ||
387 | &dns_compat), | ||
388 | GNUNET_GETOPT_OPTION_END }; | ||
389 | int ret; | ||
390 | |||
391 | if (GNUNET_OK != | ||
392 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
393 | &argc, &argv)) | ||
394 | return 2; | ||
395 | |||
396 | GNUNET_log_setup ("gnunet-gns", "WARNING", NULL); | ||
397 | ret = GNUNET_PROGRAM_run (argc, | ||
398 | argv, | ||
399 | "gnunet-gns", | ||
400 | _ ("GNUnet GNS resolver tool"), | ||
401 | options, | ||
402 | &run, | ||
403 | NULL); | ||
404 | GNUNET_free_nz ((void *) argv); | ||
405 | if (GNUNET_OK != ret) | ||
406 | return 1; | ||
407 | return global_ret; | ||
408 | } | ||
409 | |||
410 | |||
411 | /* end of gnunet-gns.c */ | ||
diff --git a/src/cli/gns/meson.build b/src/cli/gns/meson.build new file mode 100644 index 000000000..bb6bfc477 --- /dev/null +++ b/src/cli/gns/meson.build | |||
@@ -0,0 +1,69 @@ | |||
1 | configure_file(input : 'gnunet-gns-proxy-setup-ca.in', | ||
2 | output : 'gnunet-gns-proxy-setup-ca', | ||
3 | configuration : cdata, | ||
4 | install: true, | ||
5 | install_mode: 'rwxr-xr-x', | ||
6 | install_dir: get_option('bindir')) | ||
7 | |||
8 | install_data('gnunet-gns-proxy-ca.template', | ||
9 | install_dir: get_option('datadir')/'gnunet') | ||
10 | install_data('openssl.cnf', | ||
11 | install_dir: get_option('datadir')/'gnunet') | ||
12 | |||
13 | executable ('gnunet-gns', | ||
14 | 'gnunet-gns.c', | ||
15 | dependencies: [libgnunetgns_dep, | ||
16 | libgnunetgnsrecord_dep, | ||
17 | idn_dep, | ||
18 | libgnunetutil_dep], | ||
19 | include_directories: [incdir, configuration_inc], | ||
20 | install: true, | ||
21 | install_dir: get_option('bindir')) | ||
22 | |||
23 | testgns = [ | ||
24 | 'test_dns2gns', | ||
25 | 'test_gns_at_lookup', | ||
26 | 'test_gns_caa_lookup', | ||
27 | 'test_gns_config_lookup', | ||
28 | 'test_gns_delegated_lookup', | ||
29 | 'test_gns_dht_lookup', | ||
30 | 'test_gns_gns2dns_cname_lookup', | ||
31 | 'test_gns_gns2dns_lookup', | ||
32 | 'test_gns_gns2dns_zkey_lookup', | ||
33 | 'test_gns_ipv6_lookup', | ||
34 | 'test_gns_lookup', | ||
35 | 'test_gns_mx_lookup', | ||
36 | 'test_gns_quickupdate', | ||
37 | 'test_gns_redirect_lookup', | ||
38 | 'test_gns_rel_expiration', | ||
39 | 'test_gns_revocation', | ||
40 | 'test_gns_soa_lookup', | ||
41 | 'test_gns_txt_lookup', | ||
42 | 'test_gns_zkey_lookup', | ||
43 | 'test_gns_sbox_simple', | ||
44 | 'test_gns_sbox', | ||
45 | 'test_gns_box_sbox', | ||
46 | ] | ||
47 | |||
48 | testconfigs = [ | ||
49 | 'test_dns2gns.conf', | ||
50 | 'test_gns_defaults.conf', | ||
51 | 'test_gns_lookup.conf', | ||
52 | 'test_gns_lookup_peer1.conf', | ||
53 | 'test_gns_lookup_peer2.conf', | ||
54 | 'test_gns_simple_lookup.conf' | ||
55 | ] | ||
56 | |||
57 | foreach f : testconfigs | ||
58 | configure_file(input: f, output: f, copy: true) | ||
59 | endforeach | ||
60 | |||
61 | foreach t : testgns | ||
62 | |||
63 | test_filename = t + '.sh' | ||
64 | test_file = configure_file(input : test_filename, | ||
65 | output : test_filename, | ||
66 | copy: true) | ||
67 | |||
68 | test(t, test_file, suite: 'gns', workdir: meson.current_build_dir(), is_parallel: false) | ||
69 | endforeach | ||
diff --git a/src/cli/gns/openssl.cnf b/src/cli/gns/openssl.cnf new file mode 100644 index 000000000..5dce35388 --- /dev/null +++ b/src/cli/gns/openssl.cnf | |||
@@ -0,0 +1,244 @@ | |||
1 | # | ||
2 | # OpenSSL example configuration file. | ||
3 | # This is mostly being used for generation of certificate requests. | ||
4 | # | ||
5 | |||
6 | # This definition stops the following lines choking if HOME isn't | ||
7 | # defined. | ||
8 | HOME = . | ||
9 | #RANDFILE = $ENV::HOME/.rnd | ||
10 | |||
11 | # Extra OBJECT IDENTIFIER info: | ||
12 | #oid_file = $ENV::HOME/.oid | ||
13 | oid_section = new_oids | ||
14 | |||
15 | # To use this configuration file with the "-extfile" option of the | ||
16 | # "openssl x509" utility, name here the section containing the | ||
17 | # X.509v3 extensions to use: | ||
18 | # extensions = | ||
19 | # (Alternatively, use a configuration file that has only | ||
20 | # X.509v3 extensions in its main [= default] section.) | ||
21 | |||
22 | [ new_oids ] | ||
23 | |||
24 | # We can add new OIDs in here for use by 'ca' and 'req'. | ||
25 | # Add a simple OID like this: | ||
26 | # testoid1=1.2.3.4 | ||
27 | # Or use config file substitution like this: | ||
28 | # testoid2=${testoid1}.5.6 | ||
29 | |||
30 | #################################################################### | ||
31 | [ ca ] | ||
32 | default_ca = CA_default # The default ca section | ||
33 | |||
34 | #################################################################### | ||
35 | [ CA_default ] | ||
36 | |||
37 | dir = ./demoCA # Where everything is kept | ||
38 | certs = $dir/certs # Where the issued certs are kept | ||
39 | crl_dir = $dir/crl # Where the issued crl are kept | ||
40 | database = $dir/index.txt # database index file. | ||
41 | new_certs_dir = $dir/newcerts # default place for new certs. | ||
42 | |||
43 | certificate = $dir/cacert.pem # The CA certificate | ||
44 | serial = $dir/serial # The current serial number | ||
45 | crl = $dir/crl.pem # The current CRL | ||
46 | private_key = $dir/private/cakey.pem# The private key | ||
47 | RANDFILE = $dir/private/.rand # private random number file | ||
48 | |||
49 | x509_extensions = usr_cert # The extensions to add to the cert | ||
50 | |||
51 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs | ||
52 | # so this is commented out by default to leave a V1 CRL. | ||
53 | # crl_extensions = crl_ext | ||
54 | |||
55 | default_days = 365 # how long to certify for | ||
56 | default_crl_days= 30 # how long before next CRL | ||
57 | default_md = md5 # which md to use. | ||
58 | preserve = no # keep passed DN ordering | ||
59 | |||
60 | # A few difference way of specifying how similar the request should look | ||
61 | # For type CA, the listed attributes must be the same, and the optional | ||
62 | # and supplied fields are just that :-) | ||
63 | policy = policy_match | ||
64 | |||
65 | # For the CA policy | ||
66 | [ policy_match ] | ||
67 | countryName = match | ||
68 | stateOrProvinceName = match | ||
69 | organizationName = match | ||
70 | organizationalUnitName = optional | ||
71 | commonName = supplied | ||
72 | emailAddress = optional | ||
73 | |||
74 | # For the 'anything' policy | ||
75 | # At this point in time, you must list all acceptable 'object' | ||
76 | # types. | ||
77 | [ policy_anything ] | ||
78 | countryName = optional | ||
79 | stateOrProvinceName = optional | ||
80 | localityName = optional | ||
81 | organizationName = optional | ||
82 | organizationalUnitName = optional | ||
83 | commonName = supplied | ||
84 | emailAddress = optional | ||
85 | |||
86 | #################################################################### | ||
87 | [ req ] | ||
88 | default_bits = 1024 | ||
89 | default_keyfile = privkey.pem | ||
90 | distinguished_name = req_distinguished_name | ||
91 | attributes = req_attributes | ||
92 | x509_extensions = v3_ca # The extensions to add to the self signed cert | ||
93 | |||
94 | # Passwords for private keys if not present they will be prompted for | ||
95 | # input_password = secret | ||
96 | # output_password = secret | ||
97 | |||
98 | # This sets a mask for permitted string types. There are several options. | ||
99 | # default: PrintableString, T61String, BMPString. | ||
100 | # pkix : PrintableString, BMPString. | ||
101 | # utf8only: only UTF8Strings. | ||
102 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). | ||
103 | # MASK:XXXX a literal mask value. | ||
104 | # WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings | ||
105 | # so use this option with caution! | ||
106 | string_mask = nombstr | ||
107 | |||
108 | # req_extensions = v3_req # The extensions to add to a certificate request | ||
109 | |||
110 | [ req_distinguished_name ] | ||
111 | countryName = Country Name (2 letter code) | ||
112 | countryName_default = AU | ||
113 | countryName_min = 2 | ||
114 | countryName_max = 2 | ||
115 | |||
116 | stateOrProvinceName = State or Province Name (full name) | ||
117 | stateOrProvinceName_default = Some-State | ||
118 | |||
119 | localityName = Locality Name (eg, city) | ||
120 | |||
121 | 0.organizationName = Organization Name (eg, company) | ||
122 | 0.organizationName_default = Internet Widgits Pty Ltd | ||
123 | |||
124 | # we can do this but it is not needed normally :-) | ||
125 | #1.organizationName = Second Organization Name (eg, company) | ||
126 | #1.organizationName_default = World Wide Web Pty Ltd | ||
127 | |||
128 | organizationalUnitName = Organizational Unit Name (eg, section) | ||
129 | #organizationalUnitName_default = | ||
130 | |||
131 | commonName = Common Name (eg, YOUR name) | ||
132 | commonName_max = 64 | ||
133 | |||
134 | emailAddress = Email Address | ||
135 | emailAddress_max = 40 | ||
136 | |||
137 | # SET-ex3 = SET extension number 3 | ||
138 | |||
139 | [ req_attributes ] | ||
140 | challengePassword = A challenge password | ||
141 | challengePassword_min = 4 | ||
142 | challengePassword_max = 20 | ||
143 | |||
144 | unstructuredName = An optional company name | ||
145 | |||
146 | [ usr_cert ] | ||
147 | |||
148 | # These extensions are added when 'ca' signs a request. | ||
149 | |||
150 | # This goes against PKIX guidelines but some CAs do it and some software | ||
151 | # requires this to avoid interpreting an end user certificate as a CA. | ||
152 | |||
153 | basicConstraints=CA:FALSE | ||
154 | |||
155 | # Here are some examples of the usage of nsCertType. If it is omitted | ||
156 | # the certificate can be used for anything *except* object signing. | ||
157 | |||
158 | # This is OK for an SSL server. | ||
159 | # nsCertType = server | ||
160 | |||
161 | # For an object signing certificate this would be used. | ||
162 | # nsCertType = objsign | ||
163 | |||
164 | # For normal client use this is typical | ||
165 | # nsCertType = client, email | ||
166 | |||
167 | # and for everything including object signing: | ||
168 | # nsCertType = client, email, objsign | ||
169 | |||
170 | # This is typical in keyUsage for a client certificate. | ||
171 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment | ||
172 | |||
173 | # This will be displayed in Netscape's comment listbox. | ||
174 | nsComment = "OpenSSL Generated Certificate" | ||
175 | |||
176 | # PKIX recommendations harmless if included in all certificates. | ||
177 | subjectKeyIdentifier=hash | ||
178 | authorityKeyIdentifier=keyid,issuer:always | ||
179 | |||
180 | # This stuff is for subjectAltName and issuerAltname. | ||
181 | # Import the email address. | ||
182 | # subjectAltName=email:copy | ||
183 | |||
184 | # Copy subject details | ||
185 | # issuerAltName=issuer:copy | ||
186 | |||
187 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem | ||
188 | #nsBaseUrl | ||
189 | #nsRevocationUrl | ||
190 | #nsRenewalUrl | ||
191 | #nsCaPolicyUrl | ||
192 | #nsSslServerName | ||
193 | |||
194 | [ v3_req ] | ||
195 | |||
196 | # Extensions to add to a certificate request | ||
197 | |||
198 | basicConstraints = CA:FALSE | ||
199 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment | ||
200 | |||
201 | [ v3_ca ] | ||
202 | |||
203 | |||
204 | # Extensions for a typical CA | ||
205 | |||
206 | |||
207 | # PKIX recommendation. | ||
208 | |||
209 | subjectKeyIdentifier=hash | ||
210 | |||
211 | authorityKeyIdentifier=keyid:always,issuer:always | ||
212 | |||
213 | # This is what PKIX recommends but some broken software chokes on critical | ||
214 | # extensions. | ||
215 | #basicConstraints = critical,CA:true | ||
216 | # So we do this instead. | ||
217 | basicConstraints = CA:true | ||
218 | |||
219 | # Key usage: this is typical for a CA certificate. However since it will | ||
220 | # prevent it being used as an test self-signed certificate it is best | ||
221 | # left out by default. | ||
222 | # keyUsage = cRLSign, keyCertSign | ||
223 | |||
224 | # Some might want this also | ||
225 | # nsCertType = sslCA, emailCA | ||
226 | |||
227 | # Include email address in subject alt name: another PKIX recommendation | ||
228 | # subjectAltName=email:copy | ||
229 | # Copy issuer details | ||
230 | # issuerAltName=issuer:copy | ||
231 | |||
232 | # DER hex encoding of an extension: beware experts only! | ||
233 | # obj=DER:02:03 | ||
234 | # Where 'obj' is a standard or added object | ||
235 | # You can even override a supported extension: | ||
236 | # basicConstraints= critical, DER:30:03:01:01:FF | ||
237 | |||
238 | [ crl_ext ] | ||
239 | |||
240 | # CRL extensions. | ||
241 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. | ||
242 | |||
243 | # issuerAltName=issuer:copy | ||
244 | authorityKeyIdentifier=keyid:always,issuer:always | ||
diff --git a/src/cli/gns/test_dns2gns.conf b/src/cli/gns/test_dns2gns.conf new file mode 100644 index 000000000..2f6bdc797 --- /dev/null +++ b/src/cli/gns/test_dns2gns.conf | |||
@@ -0,0 +1,69 @@ | |||
1 | @INLINE@ test_gns_defaults.conf | ||
2 | |||
3 | [namecache] | ||
4 | DISABLE = YES | ||
5 | |||
6 | [PATHS] | ||
7 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-peer-1/ | ||
8 | |||
9 | [dht] | ||
10 | START_ON_DEMAND = YES | ||
11 | IMMEDIATE_START = YES | ||
12 | |||
13 | [gns] | ||
14 | # PREFIX = valgrind --leak-check=full --track-origins=yes | ||
15 | START_ON_DEMAND = YES | ||
16 | AUTO_IMPORT_PKEY = YES | ||
17 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
18 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
19 | RECORD_PUT_INTERVAL = 1 h | ||
20 | ZONE_PUBLISH_TIME_WINDOW = 1 h | ||
21 | .gnunet.org = | ||
22 | |||
23 | [namestore] | ||
24 | IMMEDIATE_START = YES | ||
25 | #PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/ns_log | ||
26 | |||
27 | [revocation] | ||
28 | WORKBITS = 1 | ||
29 | |||
30 | [dhtcache] | ||
31 | QUOTA = 1 MB | ||
32 | DATABASE = heap | ||
33 | |||
34 | [topology] | ||
35 | TARGET-CONNECTION-COUNT = 16 | ||
36 | AUTOCONNECT = YES | ||
37 | FRIENDS-ONLY = NO | ||
38 | MINIMUM-FRIENDS = 0 | ||
39 | |||
40 | [ats] | ||
41 | WAN_QUOTA_IN = 1 GB | ||
42 | WAN_QUOTA_OUT = 1 GB | ||
43 | |||
44 | [transport] | ||
45 | plugins = tcp | ||
46 | NEIGHBOUR_LIMIT = 50 | ||
47 | PORT = 2091 | ||
48 | |||
49 | [transport-tcp] | ||
50 | TIMEOUT = 300 s | ||
51 | |||
52 | [nat] | ||
53 | DISABLEV6 = YES | ||
54 | BINDTO = 127.0.0.1 | ||
55 | ENABLE_UPNP = NO | ||
56 | BEHIND_NAT = NO | ||
57 | ALLOW_NAT = NO | ||
58 | INTERNAL_ADDRESS = 127.0.0.1 | ||
59 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
60 | |||
61 | [dns2gns] | ||
62 | BINARY = gnunet-dns2gns | ||
63 | START_ON_DEMAND = YES | ||
64 | IMMEDIATE_START = YES | ||
65 | RUN_PER_USER = YES | ||
66 | BIND_TO = 127.0.0.1 | ||
67 | BIND_TO6 = ::1 | ||
68 | PORT = 12000 | ||
69 | OPTIONS = -d 1.1.1.1 | ||
diff --git a/src/cli/gns/test_dns2gns.sh b/src/cli/gns/test_dns2gns.sh new file mode 100755 index 000000000..a6024ca3c --- /dev/null +++ b/src/cli/gns/test_dns2gns.sh | |||
@@ -0,0 +1,52 @@ | |||
1 | #!/bin/bash | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_dns2gns.conf" INT | ||
4 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
5 | |||
6 | LOCATION=$(which gnunet-config) | ||
7 | if [ -z $LOCATION ] | ||
8 | then | ||
9 | LOCATION="gnunet-config" | ||
10 | fi | ||
11 | $LOCATION --version 1> /dev/null | ||
12 | if test $? != 0 | ||
13 | then | ||
14 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
15 | exit 77 | ||
16 | fi | ||
17 | |||
18 | rm -rf `gnunet-config -c test_dns2gns.conf -f -s paths -o GNUNET_TEST_HOME` | ||
19 | MY_EGO="localego" | ||
20 | TEST_IP="127.0.0.1" | ||
21 | TEST_IPV6="dead::beef" | ||
22 | LABEL="fnord" | ||
23 | TEST_DOMAIN="taler.net" | ||
24 | |||
25 | gnunet-arm -s -c test_dns2gns.conf | ||
26 | PKEY=`gnunet-identity -V -C $MY_EGO -c test_dns2gns.conf` | ||
27 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t A -V $TEST_IP -e 3600s -c test_dns2gns.conf | ||
28 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t AAAA -V $TEST_IPV6 -e 3600s -c test_dns2gns.conf | ||
29 | |||
30 | # FIXME resolution works but always returns all available records | ||
31 | # also, the records seem to be returned twice if using GNS | ||
32 | |||
33 | if nslookup -port=12000 $LABEL.$PKEY localhost && nslookup -port=12000 $LABEL.$MY_EGO localhost; then | ||
34 | echo "PASS: GNS records can be resolved using dns2gns bridge" | ||
35 | else | ||
36 | echo "FAIL: GNS records can't be resolved using dns2gns bridge" | ||
37 | gnunet-arm -e -c test_dns2gns.conf | ||
38 | rm -rf `gnunet-config -c test_dns2gns.conf -f -s paths -o GNUNET_TEST_HOME` | ||
39 | exit 1 | ||
40 | fi | ||
41 | |||
42 | if nslookup -port=12000 $TEST_DOMAIN localhost; then | ||
43 | echo "PASS: DNS records can be resolved using dns2gns bridge" | ||
44 | else | ||
45 | echo "FAIL: DNS records can't be resolved using dns2gns bridge" | ||
46 | gnunet-arm -e -c test_dns2gns.conf | ||
47 | rm -rf `gnunet-config -c test_dns2gns.conf -f -s paths -o GNUNET_TEST_HOME` | ||
48 | exit 1 | ||
49 | fi | ||
50 | gnunet-arm -e -c test_dns2gns.conf | ||
51 | |||
52 | rm -rf `gnunet-config -c test_dns2gns.conf -f -s paths -o GNUNET_TEST_HOME` | ||
diff --git a/src/cli/gns/test_gns_at_lookup.sh b/src/cli/gns/test_gns_at_lookup.sh new file mode 100755 index 000000000..6a2c958de --- /dev/null +++ b/src/cli/gns/test_gns_at_lookup.sh | |||
@@ -0,0 +1,41 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
19 | |||
20 | TEST_IP="127.0.0.1" | ||
21 | MY_EGO="myego" | ||
22 | gnunet-arm -s -c test_gns_lookup.conf | ||
23 | gnunet-identity -C delegatedego -c test_gns_lookup.conf | ||
24 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep delegatedego | awk '{print $3}') | ||
25 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
26 | gnunet-namestore -p -z $MY_EGO -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
27 | gnunet-namestore -p -z delegatedego -a -n '@' -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
28 | sleep 0.5 | ||
29 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
30 | gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
31 | gnunet-namestore -z delegatedego -d -n '@' -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
32 | gnunet-arm -e -c test_gns_lookup.conf | ||
33 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
34 | |||
35 | if [ "$RES_IP" = "$TEST_IP" ] | ||
36 | then | ||
37 | exit 0 | ||
38 | else | ||
39 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
40 | exit 1 | ||
41 | fi | ||
diff --git a/src/cli/gns/test_gns_box_sbox.sh b/src/cli/gns/test_gns_box_sbox.sh new file mode 100755 index 000000000..d7e95912e --- /dev/null +++ b/src/cli/gns/test_gns_box_sbox.sh | |||
@@ -0,0 +1,59 @@ | |||
1 | #!/bin/bash | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | TEST_B="TXT_record_in_BOX" | ||
20 | TEST_S="TXT_record_in_SBOX" | ||
21 | TEST_A="10.1.11.10" | ||
22 | MY_EGO="myego" | ||
23 | LABEL="testsbox" | ||
24 | SERVICE="443" | ||
25 | SERVICE_TEXT="_443" | ||
26 | PROTOCOL="6" | ||
27 | PROTOCOL_TEXT="_tcp" | ||
28 | gnunet-arm -s -c test_gns_lookup.conf | ||
29 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
30 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$SERVICE_TEXT.$PROTOCOL_TEXT 16 $TEST_S" -e never -c test_gns_lookup.conf | ||
31 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "$PROTOCOL $SERVICE 16 $TEST_B" -e never -c test_gns_lookup.conf | ||
32 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$SERVICE_TEXT.$PROTOCOL_TEXT 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
33 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "$PROTOCOL $SERVICE 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
34 | sleep 0.5 | ||
35 | RES_B_S=`$DO_TIMEOUT gnunet-gns --raw -u $SERVICE_TEXT.$PROTOCOL_TEXT.$LABEL.$MY_EGO -t TXT -c test_gns_lookup.conf` | ||
36 | RES_A=`$DO_TIMEOUT gnunet-gns --raw -u $SERVICE_TEXT.$PROTOCOL_TEXT.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
37 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$SERVICE_TEXT.$PROTOCOL_TEXT 16 $TEST_S" -e never -c test_gns_lookup.conf | ||
38 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t BOX -V "$PROTOCOL $SERVICE 16 $TEST_B" -e never -c test_gns_lookup.conf | ||
39 | gnunet-namestore -p -z $MY_EGO -d -n $LABEL -t SBOX -V "$SERVICE_TEXT.$PROTOCOL_TEXT 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
40 | gnunet-namestore -p -z $MY_EGO -d -n $LABEL -t BOX -V "$PROTOCOL $SERVICE 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
41 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
42 | gnunet-arm -e -c test_gns_lookup.conf | ||
43 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
44 | |||
45 | { read RES_A1; read RES_A2; read RES_B; read RES_S;} <<< "${RES_B_S}" | ||
46 | if [ "$RES_B" = "$RES_S" ] | ||
47 | then | ||
48 | echo "Failed to resolve to diffrent TXT records, got '$RES_B' and '$RES_S'." | ||
49 | exit 1 | ||
50 | fi | ||
51 | |||
52 | { read RES_S_A; read RES_B_A;} <<< "${RES_A}" | ||
53 | if [ "$RES_S_A" = "$TEST_A" ] && [ "$RES_B_A" = "$TEST_A" ] | ||
54 | then | ||
55 | exit 0 | ||
56 | else | ||
57 | echo "Failed to resolve to proper A '$TEST_A', got '$RES_S_A' and '$RES_S_B'." | ||
58 | exit 1 | ||
59 | fi | ||
diff --git a/src/cli/gns/test_gns_caa_lookup.sh b/src/cli/gns/test_gns_caa_lookup.sh new file mode 100755 index 000000000..fb488f47b --- /dev/null +++ b/src/cli/gns/test_gns_caa_lookup.sh | |||
@@ -0,0 +1,38 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | TEST_CAA="0 issue ca.example.net; policy=ev" | ||
20 | MY_EGO="myego" | ||
21 | LABEL="testcaa" | ||
22 | gnunet-arm -s -c test_gns_lookup.conf | ||
23 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
24 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t CAA -V "$TEST_CAA" -e never -c test_gns_lookup.conf | ||
25 | sleep 0.5 | ||
26 | RES_CAA=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$MY_EGO -t CAA -c test_gns_lookup.conf` | ||
27 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t CAA -V "$TEST_CAA" -e never -c test_gns_lookup.conf | ||
28 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
29 | gnunet-arm -e -c test_gns_lookup.conf | ||
30 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
31 | |||
32 | if [ "$RES_CAA" = "$TEST_CAA" ] | ||
33 | then | ||
34 | exit 0 | ||
35 | else | ||
36 | echo "Failed to resolve to proper CAA, got '$RES_CAA'." | ||
37 | exit 1 | ||
38 | fi | ||
diff --git a/src/cli/gns/test_gns_config_lookup.sh b/src/cli/gns/test_gns_config_lookup.sh new file mode 100755 index 000000000..bda08f87b --- /dev/null +++ b/src/cli/gns/test_gns_config_lookup.sh | |||
@@ -0,0 +1,44 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | MY_EGO="myego" | ||
17 | |||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -s PATHS -o GNUNET_HOME -f` | ||
19 | CFG=`mktemp --tmpdir=$PWD` | ||
20 | cp test_gns_lookup.conf $CFG || exit 77 | ||
21 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
22 | TEST_IP="dead::beef" | ||
23 | gnunet-arm -s -c $CFG || exit 77 | ||
24 | gnunet-identity -C $MY_EGO -c $CFG | ||
25 | EPUB=`gnunet-identity -d -c $CFG | grep $MY_EGO | awk '{print $3}'` | ||
26 | gnunet-arm -e -c $CFG | ||
27 | gnunet-config -c $CFG -s "gns" -o ".google.com" -V $EPUB | ||
28 | gnunet-arm -s -c $CFG | ||
29 | sleep 1 | ||
30 | gnunet-namestore -p -z $MY_EGO -a -n www -t AAAA -V $TEST_IP -e never -c $CFG | ||
31 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.google.com -t AAAA -c $CFG` | ||
32 | gnunet-namestore -z $MY_EGO -d -n www -t AAAA -V $TEST_IP -e never -c $CFG | ||
33 | gnunet-identity -D $MY_EGO -c $CFG | ||
34 | gnunet-arm -e -c $CFG | ||
35 | rm -rf `gnunet-config -c $CFG -f -s paths -o GNUNET_TEST_HOME` | ||
36 | rm $CFG | ||
37 | |||
38 | if [ "$RES_IP" = "$TEST_IP" ] | ||
39 | then | ||
40 | exit 0 | ||
41 | else | ||
42 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
43 | exit 1 | ||
44 | fi | ||
diff --git a/src/cli/gns/test_gns_defaults.conf b/src/cli/gns/test_gns_defaults.conf new file mode 100644 index 000000000..80a2f3c44 --- /dev/null +++ b/src/cli/gns/test_gns_defaults.conf | |||
@@ -0,0 +1,34 @@ | |||
1 | [PATHS] | ||
2 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-testing/ | ||
3 | |||
4 | [namestore-sqlite] | ||
5 | FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db | ||
6 | |||
7 | [namecache-sqlite] | ||
8 | FILENAME=$GNUNET_TEST_HOME/namecache/namecache.db | ||
9 | |||
10 | [identity] | ||
11 | # Directory where we store information about our egos | ||
12 | EGODIR = $GNUNET_TEST_HOME/identity/egos/ | ||
13 | |||
14 | [dhtcache] | ||
15 | DATABASE = heap | ||
16 | |||
17 | [transport] | ||
18 | PLUGINS = tcp | ||
19 | |||
20 | [transport-tcp] | ||
21 | BINDTO = 127.0.0.1 | ||
22 | |||
23 | |||
24 | [fs] | ||
25 | IMMEDIATE_START = NO | ||
26 | START_ON_DEMAND = NO | ||
27 | |||
28 | [rps] | ||
29 | IMMEDIATE_START = NO | ||
30 | START_ON_DEMAND = NO | ||
31 | |||
32 | [topology] | ||
33 | IMMEDIATE_START = NO | ||
34 | START_ON_DEMAND = NO | ||
diff --git a/src/cli/gns/test_gns_delegated_lookup.sh b/src/cli/gns/test_gns_delegated_lookup.sh new file mode 100755 index 000000000..5105abdb5 --- /dev/null +++ b/src/cli/gns/test_gns_delegated_lookup.sh | |||
@@ -0,0 +1,45 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
4 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
5 | |||
6 | LOCATION=$(which gnunet-config) | ||
7 | if [ -z $LOCATION ] | ||
8 | then | ||
9 | LOCATION="gnunet-config" | ||
10 | fi | ||
11 | $LOCATION --version 1> /dev/null | ||
12 | if test $? != 0 | ||
13 | then | ||
14 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
15 | exit 77 | ||
16 | fi | ||
17 | |||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
19 | MY_EGO="myego" | ||
20 | OTHER_EGO="delegatedego" | ||
21 | FINAL_LABEL="www" | ||
22 | DELEGATION_LABEL="b" | ||
23 | |||
24 | TEST_IP="127.0.0.1" | ||
25 | gnunet-arm -s -c test_gns_lookup.conf | ||
26 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup.conf | ||
27 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep $OTHER_EGO | awk '{print $3}') | ||
28 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
29 | gnunet-namestore -p -z $MY_EGO -a -n $DELEGATION_LABEL -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
30 | gnunet-namestore -p -z $OTHER_EGO -a -n $FINAL_LABEL -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
31 | sleep 0.5 | ||
32 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u $FINAL_LABEL.$DELEGATION_LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
33 | gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
34 | gnunet-namestore -z $OTHER_EGO -d -n $FINAL_LABEL -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
35 | gnunet-arm -e -c test_gns_lookup.conf | ||
36 | |||
37 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
38 | |||
39 | if [ "$RES_IP" = "$TEST_IP" ] | ||
40 | then | ||
41 | exit 0 | ||
42 | else | ||
43 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
44 | exit 1 | ||
45 | fi | ||
diff --git a/src/cli/gns/test_gns_dht_lookup.sh b/src/cli/gns/test_gns_dht_lookup.sh new file mode 100755 index 000000000..da87d8477 --- /dev/null +++ b/src/cli/gns/test_gns_dht_lookup.sh | |||
@@ -0,0 +1,63 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
4 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
5 | |||
6 | LOCATION=$(which gnunet-config) | ||
7 | if [ -z $LOCATION ] | ||
8 | then | ||
9 | LOCATION="gnunet-config" | ||
10 | fi | ||
11 | $LOCATION --version 1> /dev/null | ||
12 | if test $? != 0 | ||
13 | then | ||
14 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
15 | exit 77 | ||
16 | fi | ||
17 | |||
18 | TEST_IP="127.0.0.1" | ||
19 | MY_EGO="myego" | ||
20 | OTHER_EGO="delegatedego" | ||
21 | |||
22 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
23 | gnunet-arm -s -c test_gns_lookup.conf | ||
24 | gnunet-arm -i zonemaster -c test_gns_lookup.conf | ||
25 | gnunet-arm -i datastore -c test_gns_lookup.conf | ||
26 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup.conf | ||
27 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep $OTHER_EGO | awk '{print $3}') | ||
28 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
29 | echo "MYEGO: $MY_EGO OTHER_EGO: $DELEGATED_PKEY" | ||
30 | gnunet-namestore -p -z $MY_EGO -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
31 | #This works | ||
32 | gnunet-namestore -p -z $OTHER_EGO -a -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
33 | #This doesn't | ||
34 | gnunet-namestore -p -z $OTHER_EGO -a -n www2 -t A -V $TEST_IP -e '5 s' -c test_gns_lookup.conf | ||
35 | sleep 6 | ||
36 | #gnunet-namestore -p -z $OTHER_EGO -d -n www2 -t A -V $TEST_IP -e '5 s' -c test_gns_lookup.conf | ||
37 | #gnunet-namestore -p -z $OTHER_EGO -a -n www2 -t A -V $TEST_IP -e '5 s' -c test_gns_lookup.conf | ||
38 | gnunet-arm -k zonemaster -c test_gns_lookup.conf | ||
39 | gnunet-arm -i zonemaster -c test_gns_lookup.conf | ||
40 | #gnunet-arm -r -c test_gns_lookup.conf | ||
41 | #gnunet-arm -i zonemaster | ||
42 | #gnunet-arm -i gns -c test_gns_lookup.conf | ||
43 | #gnunet-identity -D $OTHER_EGO -c test_gns_lookup.conf | ||
44 | #gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
45 | #gnunet-namestore -z $OTHER_EGO -d -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
46 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
47 | RES_IP_REL=`$DO_TIMEOUT gnunet-gns --raw -u www2.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
48 | #gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
49 | gnunet-arm -e -c test_gns_lookup.conf | ||
50 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
51 | |||
52 | if [ "$RES_IP_REL" != "$TEST_IP" ] | ||
53 | then | ||
54 | echo "Failed to resolve to proper IP, got $RES_IP_REL. (relative expiration)" | ||
55 | #exit 1 | ||
56 | fi | ||
57 | if [ "$RES_IP" = "$TEST_IP" ] | ||
58 | then | ||
59 | exit 0 | ||
60 | else | ||
61 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
62 | exit 1 | ||
63 | fi | ||
diff --git a/src/cli/gns/test_gns_gns2dns_cname_lookup.sh b/src/cli/gns/test_gns_gns2dns_cname_lookup.sh new file mode 100755 index 000000000..c33c9d132 --- /dev/null +++ b/src/cli/gns/test_gns_gns2dns_cname_lookup.sh | |||
@@ -0,0 +1,98 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | # IP address of 'www.gnunet.org' | ||
19 | TEST_IP="147.87.255.218" | ||
20 | # IP address of 'gnunet.org' | ||
21 | TEST_IPALT="131.159.74.67" | ||
22 | # IPv6 address of 'gnunet.org' | ||
23 | TEST_IP6="2a07:6b47:100:464::9357:ffdb" | ||
24 | |||
25 | # main label used during resolution | ||
26 | TEST_RECORD_NAME="homepage" | ||
27 | |||
28 | XNS=ns.joker.com | ||
29 | |||
30 | if ! nslookup gnunet.org a.$XNS > /dev/null 2>&1 | ||
31 | then | ||
32 | echo "Cannot reach DNS, skipping test" | ||
33 | exit 77 | ||
34 | fi | ||
35 | |||
36 | # helper record for pointing to the DNS resolver | ||
37 | TEST_RESOLVER_LABEL="resolver" | ||
38 | # GNS2DNS record value: delegate to DNS domain 'gnunet.org' | ||
39 | # using the TEST_RESOLVER_LABEL DNS server for resolution | ||
40 | TEST_RECORD_GNS2DNS1="gnunet.org@a.$XNS" | ||
41 | TEST_RECORD_GNS2DNS2="gnunet.org@b.$XNS" | ||
42 | TEST_RECORD_GNS2DNS3="gnunet.org@c.$XNS" | ||
43 | |||
44 | MY_EGO="myego" | ||
45 | # various names we will use for resolution | ||
46 | TEST_DOMAIN="www.${TEST_RECORD_NAME}.$MY_EGO" | ||
47 | |||
48 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
49 | |||
50 | gnunet-arm -s -c test_gns_lookup.conf | ||
51 | OUT=`$DO_TIMEOUT gnunet-resolver -c test_gns_lookup.conf www.gnunet.org` | ||
52 | echo $OUT | grep $TEST_IP - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv4 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
53 | echo $OUT | grep $TEST_IP6 - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv6 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
54 | |||
55 | |||
56 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
57 | |||
58 | # set IP address for DNS resolver for resolving in gnunet.org domain | ||
59 | # map '$TEST_RECORD_NAME.$MY_EGO' to 'gnunet.org' in DNS | ||
60 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS1 -e never -c test_gns_lookup.conf | ||
61 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS2 -e never -c test_gns_lookup.conf | ||
62 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS3 -e never -c test_gns_lookup.conf | ||
63 | |||
64 | gnunet-namestore -z $MY_EGO -D -c test_gns_lookup.conf | ||
65 | |||
66 | sleep 0.5 | ||
67 | |||
68 | # lookup 'www.gnunet.org', IPv4 | ||
69 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t A -c test_gns_lookup.conf` | ||
70 | # lookup 'www.gnunet.org', IPv6 | ||
71 | RES_IP6=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t AAAA -c test_gns_lookup.conf` | ||
72 | |||
73 | # clean up | ||
74 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS1 -e never -c test_gns_lookup.conf | ||
75 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS2 -e never -c test_gns_lookup.conf | ||
76 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS3 -e never -c test_gns_lookup.conf | ||
77 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
78 | gnunet-arm -e -c test_gns_lookup.conf | ||
79 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
80 | |||
81 | ret=0 | ||
82 | if echo "$RES_IP" | grep "$TEST_IP" > /dev/null | ||
83 | then | ||
84 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP." | ||
85 | else | ||
86 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP, wanted $TEST_IP." | ||
87 | ret=1 | ||
88 | fi | ||
89 | |||
90 | if echo "$RES_IP6" | grep "$TEST_IP6" > /dev/null | ||
91 | then | ||
92 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP6." | ||
93 | else | ||
94 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP6, wanted $TEST_IP6." | ||
95 | ret=1 | ||
96 | fi | ||
97 | |||
98 | exit $ret | ||
diff --git a/src/cli/gns/test_gns_gns2dns_lookup.sh b/src/cli/gns/test_gns_gns2dns_lookup.sh new file mode 100755 index 000000000..43a4756d3 --- /dev/null +++ b/src/cli/gns/test_gns_gns2dns_lookup.sh | |||
@@ -0,0 +1,117 @@ | |||
1 | #!/bin/sh | ||
2 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
17 | # IP address of 'docs.gnunet.org' | ||
18 | TEST_IP_ALT2="147.87.255.218" | ||
19 | # IP address of 'www.gnunet.org' | ||
20 | TEST_IP="147.87.255.218" | ||
21 | # IPv6 address of 'gnunet.org' | ||
22 | TEST_IP6="2a07:6b47:100:464::9357:ffdb" | ||
23 | # permissive DNS resolver we will use for the test | ||
24 | TEST_IP_GNS2DNS="8.8.8.8" | ||
25 | |||
26 | # main label used during resolution | ||
27 | TEST_RECORD_NAME="homepage" | ||
28 | |||
29 | if ! nslookup gnunet.org $TEST_IP_GNS2DNS > /dev/null 2>&1 | ||
30 | then | ||
31 | echo "Cannot reach DNS, skipping test" | ||
32 | exit 77 | ||
33 | fi | ||
34 | |||
35 | # helper record for pointing to the DNS resolver | ||
36 | TEST_RESOLVER_LABEL="resolver" | ||
37 | # GNS2DNS record value: delegate to DNS domain 'gnunet.org' | ||
38 | # using the TEST_RESOLVER_LABEL DNS server for resolution | ||
39 | TEST_RECORD_GNS2DNS="gnunet.org@${TEST_RESOLVER_LABEL}.+" | ||
40 | |||
41 | MY_EGO="myego" | ||
42 | # various names we will use for resolution | ||
43 | TEST_DOMAIN="www.${TEST_RECORD_NAME}.$MY_EGO" | ||
44 | TEST_DOMAIN_ALT="${TEST_RECORD_NAME}.$MY_EGO" | ||
45 | TEST_DOMAIN_ALT2="docs.${TEST_RECORD_NAME}.$MY_EGO" | ||
46 | |||
47 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
48 | |||
49 | |||
50 | gnunet-arm -s -c test_gns_lookup.conf | ||
51 | |||
52 | OUT=`$DO_TIMEOUT gnunet-resolver -c test_gns_lookup.conf www.gnunet.org` | ||
53 | echo $OUT | grep $TEST_IP - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv4 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
54 | echo $OUT | grep $TEST_IP6 - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv6 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
55 | |||
56 | |||
57 | |||
58 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
59 | |||
60 | # set IP address for DNS resolver for resolving in gnunet.org domain | ||
61 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RESOLVER_LABEL -t A -V $TEST_IP_GNS2DNS -e never -c test_gns_lookup.conf | ||
62 | # map '$TEST_RECORD_NAME.$MY_EGO' to 'gnunet.org' in DNS | ||
63 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS -e never -c test_gns_lookup.conf | ||
64 | |||
65 | sleep 1 | ||
66 | |||
67 | gnunet-gns -u $TEST_RECORD_NAME.$MY_EGO -t GNS2DNS -c test_gns_lookup.conf | ||
68 | |||
69 | # lookup 'www.gnunet.org', IPv4 | ||
70 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t A -c test_gns_lookup.conf` | ||
71 | # lookup 'www.gnunet.org', IPv6 | ||
72 | RES_IP6=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t AAAA -c test_gns_lookup.conf | head -n1` | ||
73 | # lookup 'gnunet.org', IPv4 | ||
74 | RES_IP_ALT=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_ALT -t A -c test_gns_lookup.conf` | ||
75 | # lookup 'docs.gnunet.org', IPv4 | ||
76 | RES_IP_ALT2=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_ALT2 -t A -c test_gns_lookup.conf` | ||
77 | |||
78 | # clean up | ||
79 | gnunet-namestore -z $MY_EGO -d -n $TEST_RESOLVER_LABEL -t A -V $TEST_IP_GNS2DNS -e never -c test_gns_lookup.conf | ||
80 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS -e never -c test_gns_lookup.conf | ||
81 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
82 | gnunet-arm -e -c test_gns_lookup.conf | ||
83 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
84 | |||
85 | ret=0 | ||
86 | if echo "$RES_IP" | grep "$TEST_IP" > /dev/null | ||
87 | then | ||
88 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP." | ||
89 | else | ||
90 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP, wanted $TEST_IP." | ||
91 | ret=1 | ||
92 | fi | ||
93 | |||
94 | if [ "${RES_IP6%?}" = "${TEST_IP6%?}" ] | ||
95 | then | ||
96 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP6." | ||
97 | else | ||
98 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP6, wanted $TEST_IP6." | ||
99 | ret=1 | ||
100 | fi | ||
101 | |||
102 | if echo "$RES_IP_ALT" | grep "$TEST_IP" > /dev/null | ||
103 | then | ||
104 | echo "PASS: Resolved $TEST_DOMAIN_ALT to $RES_IP_ALT." | ||
105 | else | ||
106 | echo "Failed to resolve to proper IP for $TEST_DOMAIN_ALT, got $RES_IP_ALT, wanted $TEST_IP." | ||
107 | ret=1 | ||
108 | fi | ||
109 | |||
110 | if echo "$RES_IP_ALT2" | grep "$TEST_IP_ALT2" > /dev/null | ||
111 | then | ||
112 | echo "PASS: Resolved $TEST_DOMAIN_ALT2 to $RES_IP_ALT2." | ||
113 | else | ||
114 | echo "Failed to resolve to proper IP for $TEST_DOMAIN_ALT2, got $RES_IP_ALT2, wanted $TEST_IP_ALT2." | ||
115 | ret=1 | ||
116 | fi | ||
117 | exit $ret | ||
diff --git a/src/cli/gns/test_gns_gns2dns_zkey_lookup.sh b/src/cli/gns/test_gns_gns2dns_zkey_lookup.sh new file mode 100755 index 000000000..03549314e --- /dev/null +++ b/src/cli/gns/test_gns_gns2dns_zkey_lookup.sh | |||
@@ -0,0 +1,116 @@ | |||
1 | #!/bin/sh | ||
2 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
17 | # IP address of 'docs.gnunet.org' | ||
18 | TEST_IP_ALT2="147.87.255.218" | ||
19 | # IP address of 'www.gnunet.org' | ||
20 | TEST_IP="147.87.255.218" | ||
21 | # IPv6 address of 'gnunet.org' | ||
22 | TEST_IP6="2a07:6b47:100:464::9357:ffdb" | ||
23 | # permissive DNS resolver we will use for the test | ||
24 | TEST_IP_GNS2DNS="8.8.8.8" | ||
25 | |||
26 | # main label used during resolution | ||
27 | TEST_RECORD_NAME="homepage" | ||
28 | |||
29 | if ! nslookup gnunet.org $TEST_IP_GNS2DNS > /dev/null 2>&1 | ||
30 | then | ||
31 | echo "Cannot reach DNS, skipping test" | ||
32 | exit 77 | ||
33 | fi | ||
34 | |||
35 | # helper record for pointing to the DNS resolver | ||
36 | TEST_RESOLVER_LABEL="resolver" | ||
37 | |||
38 | MY_EGO="myego" | ||
39 | # various names we will use for resolution | ||
40 | TEST_DOMAIN="www.${TEST_RECORD_NAME}.$MY_EGO" | ||
41 | TEST_DOMAIN_ALT="${TEST_RECORD_NAME}.$MY_EGO" | ||
42 | TEST_DOMAIN_ALT2="docs.${TEST_RECORD_NAME}.$MY_EGO" | ||
43 | |||
44 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
45 | |||
46 | |||
47 | gnunet-arm -s -c test_gns_lookup.conf | ||
48 | |||
49 | OUT=`$DO_TIMEOUT gnunet-resolver -c test_gns_lookup.conf www.gnunet.org` | ||
50 | echo $OUT | grep $TEST_IP - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv4 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
51 | echo $OUT | grep $TEST_IP6 - > /dev/null || { gnunet-arm -e -c test_gns_lookup.conf ; echo "IPv6 for gnunet.org not found ($OUT), skipping test"; exit 77; } | ||
52 | |||
53 | |||
54 | |||
55 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
56 | MY_EGO_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep ${MY_EGO} | awk '{print $3}') | ||
57 | # GNS2DNS record value: delegate to DNS domain 'gnunet.org' | ||
58 | # using the TEST_RESOLVER_LABEL DNS server for resolution | ||
59 | TEST_RECORD_GNS2DNS="gnunet.org@${TEST_RESOLVER_LABEL}.${MY_EGO_PKEY}" | ||
60 | |||
61 | # set IP address for DNS resolver for resolving in gnunet.org domain | ||
62 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RESOLVER_LABEL -t A -V $TEST_IP_GNS2DNS -e never -c test_gns_lookup.conf | ||
63 | # map '$TEST_RECORD_NAME.$MY_EGO' to 'gnunet.org' in DNS | ||
64 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS -e never -c test_gns_lookup.conf | ||
65 | |||
66 | sleep 1 | ||
67 | |||
68 | # lookup 'www.gnunet.org', IPv4 | ||
69 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t A -c test_gns_lookup.conf` | ||
70 | # lookup 'www.gnunet.org', IPv6 | ||
71 | RES_IP6=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t AAAA -c test_gns_lookup.conf | head -n1` | ||
72 | # lookup 'gnunet.org', IPv4 | ||
73 | RES_IP_ALT=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_ALT -t A -c test_gns_lookup.conf` | ||
74 | # lookup 'docs.gnunet.org', IPv4 | ||
75 | RES_IP_ALT2=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_ALT2 -t A -c test_gns_lookup.conf` | ||
76 | |||
77 | # clean up | ||
78 | gnunet-namestore -z $MY_EGO -d -n $TEST_RESOLVER_LABEL -t A -V $TEST_IP_GNS2DNS -e never -c test_gns_lookup.conf | ||
79 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V $TEST_RECORD_GNS2DNS -e never -c test_gns_lookup.conf | ||
80 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
81 | gnunet-arm -e -c test_gns_lookup.conf | ||
82 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
83 | |||
84 | ret=0 | ||
85 | if echo "$RES_IP" | grep "$TEST_IP" > /dev/null | ||
86 | then | ||
87 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP." | ||
88 | else | ||
89 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP, wanted $TEST_IP." | ||
90 | ret=1 | ||
91 | fi | ||
92 | |||
93 | if [ "${RES_IP6%?}" = "${TEST_IP6%?}" ] | ||
94 | then | ||
95 | echo "PASS: Resolved $TEST_DOMAIN to $RES_IP6." | ||
96 | else | ||
97 | echo "Failed to resolve to proper IP for $TEST_DOMAIN, got $RES_IP6, wanted $TEST_IP6." | ||
98 | ret=1 | ||
99 | fi | ||
100 | |||
101 | if echo "$RES_IP_ALT" | grep "$TEST_IP" > /dev/null | ||
102 | then | ||
103 | echo "PASS: Resolved $TEST_DOMAIN_ALT to $RES_IP_ALT." | ||
104 | else | ||
105 | echo "Failed to resolve to proper IP for $TEST_DOMAIN_ALT, got $RES_IP_ALT, wanted $TEST_IP." | ||
106 | ret=1 | ||
107 | fi | ||
108 | |||
109 | if echo "$RES_IP_ALT2" | grep "$TEST_IP_ALT2" > /dev/null | ||
110 | then | ||
111 | echo "PASS: Resolved $TEST_DOMAIN_ALT2 to $RES_IP_ALT2." | ||
112 | else | ||
113 | echo "Failed to resolve to proper IP for $TEST_DOMAIN_ALT2, got $RES_IP_ALT2, wanted $TEST_IP_ALT2." | ||
114 | ret=1 | ||
115 | fi | ||
116 | exit $ret | ||
diff --git a/src/cli/gns/test_gns_ipv6_lookup.sh b/src/cli/gns/test_gns_ipv6_lookup.sh new file mode 100755 index 000000000..31e662f68 --- /dev/null +++ b/src/cli/gns/test_gns_ipv6_lookup.sh | |||
@@ -0,0 +1,37 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | MY_EGO="myego" | ||
17 | |||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -s PATHS -o GNUNET_HOME -f` | ||
19 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
20 | TEST_IP="dead::beef" | ||
21 | gnunet-arm -s -c test_gns_lookup.conf | ||
22 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
23 | gnunet-namestore -p -z $MY_EGO -a -n www -t AAAA -V $TEST_IP -e never -c test_gns_lookup.conf | ||
24 | sleep 0.5 | ||
25 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.$MY_EGO -t AAAA -c test_gns_lookup.conf` | ||
26 | gnunet-namestore -z $MY_EGO -d -n www -t AAAA -V $TEST_IP -e never -c test_gns_lookup.conf | ||
27 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
28 | gnunet-arm -e -c test_gns_lookup.conf | ||
29 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
30 | |||
31 | if [ "$RES_IP" = "$TEST_IP" ] | ||
32 | then | ||
33 | exit 0 | ||
34 | else | ||
35 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
36 | exit 1 | ||
37 | fi | ||
diff --git a/src/cli/gns/test_gns_lightest.sh b/src/cli/gns/test_gns_lightest.sh new file mode 100755 index 000000000..2d2203e66 --- /dev/null +++ b/src/cli/gns/test_gns_lightest.sh | |||
@@ -0,0 +1,141 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | START_EGO="startego" | ||
20 | MY_EGO="test-lightest" | ||
21 | LABEL="test-scheme" | ||
22 | PTR_LABEL="test-ptr" | ||
23 | TEST_URI="10 1 \"https://ec.europa.eu/tools/lotl/eu-lotl.xml\"" | ||
24 | TEST_SMIMEA="3 0 1 f7e8e4e554fb7c7a8f6f360e0ca2f59d466c8f9539a25963f5ed37e905f0c797" | ||
25 | SCHEME="_scheme" | ||
26 | TRUST="_trust" | ||
27 | TRANSLATION="_translation" | ||
28 | TEST_PTR="$SCHEME.$TRUST.$LABEL.$MY_EGO.$START_EGO" | ||
29 | TEST_PTR2="$TRANSLATION.$TRUST.$LABEL.$MY_EGO.$START_EGO" | ||
30 | gnunet-arm -s -c test_gns_lookup.conf | ||
31 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
32 | gnunet-identity -C $START_EGO -c test_gns_lookup.conf | ||
33 | PKEY=`gnunet-identity -d -e $MY_EGO -q -c test_gns_lookup.conf` | ||
34 | gnunet-namestore -p -z $MY_EGO -a -n $PTR_LABEL -t BOX -V "49152 49152 12 $TEST_PTR" -e never -c test_gns_lookup.conf | ||
35 | gnunet-namestore -p -z $MY_EGO -a -n $PTR_LABEL -t BOX -V "49152 49153 12 $TEST_PTR2" -e never -c test_gns_lookup.conf | ||
36 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49152 256 $TEST_URI" -e never -c test_gns_lookup.conf | ||
37 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49152 53 $TEST_SMIMEA" -e never -c test_gns_lookup.conf | ||
38 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49153 256 $TEST_URI" -e never -c test_gns_lookup.conf | ||
39 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49153 53 $TEST_SMIMEA" -e never -c test_gns_lookup.conf | ||
40 | gnunet-namestore -p -z $START_EGO -a -n $MY_EGO -t PKEY -V "$PKEY" -e never -c test_gns_lookup.conf | ||
41 | sleep 0.5 | ||
42 | PTR_SCHEME=`$DO_TIMEOUT gnunet-gns --raw -u $SCHEME.$TRUST.$PTR_LABEL.$MY_EGO.$START_EGO -t PTR -c test_gns_lookup.conf` | ||
43 | PTR_TRANSLATION=`$DO_TIMEOUT gnunet-gns --raw -u $TRANSLATION.$TRUST.$PTR_LABEL.$MY_EGO.$START_EGO -t PTR -c test_gns_lookup.conf` | ||
44 | |||
45 | SUCCESS=0 | ||
46 | if [ "$PTR_SCHEME" != "$TEST_PTR" ] | ||
47 | then | ||
48 | echo "Failed to resolve to proper PTR, got '$PTR_SCHEME'." | ||
49 | SUCCESS=1 | ||
50 | else | ||
51 | echo "Resolved to proper PTR, got '$PTR_SCHEME'." | ||
52 | fi | ||
53 | |||
54 | if [ "$PTR_TRANSLATION" != "$TEST_PTR2" ] | ||
55 | then | ||
56 | echo "Failed to resolve to proper PTR, got '$PTR_TRANSLATION'." | ||
57 | SUCCESS=1 | ||
58 | else | ||
59 | echo "Resolved to proper PTR, got '$PTR_TRANSLATION'." | ||
60 | fi | ||
61 | |||
62 | if [ "$SUCCESS" = "1" ] | ||
63 | then | ||
64 | gnunet-namestore -z $MY_EGO -X -c test_gns_lookup.conf | ||
65 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
66 | gnunet-arm -e -c test_gns_lookup.conf | ||
67 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
68 | exit 1 | ||
69 | fi | ||
70 | |||
71 | |||
72 | RES_URI_SCHEME=`$DO_TIMEOUT gnunet-gns --raw -u $PTR_SCHEME -t URI -c test_gns_lookup.conf` | ||
73 | RES_SMIMEA_SCHEME=`$DO_TIMEOUT gnunet-gns --raw -u $PTR_SCHEME -t SMIMEA -c test_gns_lookup.conf` | ||
74 | |||
75 | RES_URI_TRANSLATION=`$DO_TIMEOUT gnunet-gns --raw -u $PTR_TRANSLATION -t URI -c test_gns_lookup.conf` | ||
76 | RES_SMIMEA_TRANSLATION=`$DO_TIMEOUT gnunet-gns --raw -u $PTR_TRANSLATION -t SMIMEA -c test_gns_lookup.conf` | ||
77 | |||
78 | |||
79 | if [ "$RES_URI_SCHEME" != "$TEST_URI" ] | ||
80 | then | ||
81 | echo "Failed to resolve to proper URI, got '$RES_URI_SCHEME'." | ||
82 | SUCCESS=1 | ||
83 | else | ||
84 | echo "Resolved to proper URI, got '$RES_URI_SCHEME'." | ||
85 | fi | ||
86 | |||
87 | if [ "$RES_SMIMEA_SCHEME" != "$TEST_SMIMEA" ] | ||
88 | then | ||
89 | echo "Failed to resolve to proper SMIMEA, got '$RES_SMIMEA_SCHEME'." | ||
90 | SUCCESS=1 | ||
91 | else | ||
92 | echo "Resolved to proper SMIMEA, got '$RES_SMIMEA_SCHEME'." | ||
93 | fi | ||
94 | |||
95 | if [ "$RES_URI_TRANSLATION" != "$TEST_URI" ] | ||
96 | then | ||
97 | echo "Failed to resolve to proper URI, got '$RES_URI_TRANSLATION'." | ||
98 | SUCCESS=1 | ||
99 | else | ||
100 | echo "Resolved to proper URI, got '$RES_URI_TRANSLATION'." | ||
101 | fi | ||
102 | |||
103 | if [ "$RES_SMIMEA_TRANSLATION" != "$TEST_SMIMEA" ] | ||
104 | then | ||
105 | echo "Failed to resolve to proper SMIMEA, got '$RES_SMIMEA_TRANSLATION'." | ||
106 | SUCCESS=1 | ||
107 | else | ||
108 | echo "Resolved to proper SMIMEA, got '$RES_SMIMEA_TRANSLATION'." | ||
109 | fi | ||
110 | |||
111 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49152 256 10 1 \"thisisnotavaliduri\"" -e never -c test_gns_lookup.conf | ||
112 | status=$? | ||
113 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t BOX -V "49152 49152 256 10 1 mailto:thisrecordismalformed@test.com" -e never -c test_gns_lookup.conf | ||
114 | status2=$? | ||
115 | |||
116 | if [ "$status" = "0" ] | ||
117 | then | ||
118 | echo "Failed to detect malformed URI." | ||
119 | SUCCESS=1 | ||
120 | else | ||
121 | echo "Detected malformed URI." | ||
122 | fi | ||
123 | |||
124 | if [ "$status2" = "0" ] | ||
125 | then | ||
126 | echo "Failed to detect malformed URI Record Presentation." | ||
127 | SUCCESS=1 | ||
128 | else | ||
129 | echo "Detected malformed URI Presentation." | ||
130 | fi | ||
131 | |||
132 | |||
133 | |||
134 | gnunet-namestore -z $MY_EGO -X -c test_gns_lookup.conf | ||
135 | gnunet-namestore -z $START_EGO -X -c test_gns_lookup.conf | ||
136 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
137 | gnunet-identity -D $START_EGO -c test_gns_lookup.conf | ||
138 | gnunet-arm -e -c test_gns_lookup.conf | ||
139 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
140 | |||
141 | exit $SUCCESS \ No newline at end of file | ||
diff --git a/src/cli/gns/test_gns_lookup.conf b/src/cli/gns/test_gns_lookup.conf new file mode 100644 index 000000000..46e89a64d --- /dev/null +++ b/src/cli/gns/test_gns_lookup.conf | |||
@@ -0,0 +1,65 @@ | |||
1 | @INLINE@ test_gns_defaults.conf | ||
2 | |||
3 | [namecache] | ||
4 | DISABLE = NO | ||
5 | |||
6 | [PATHS] | ||
7 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-peer-1/ | ||
8 | |||
9 | [dht] | ||
10 | START_ON_DEMAND = YES | ||
11 | |||
12 | [gns] | ||
13 | # PREFIX = valgrind --leak-check=full --track-origins=yes | ||
14 | START_ON_DEMAND = YES | ||
15 | AUTO_IMPORT_PKEY = YES | ||
16 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
17 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
18 | RECORD_PUT_INTERVAL = 1 h | ||
19 | ZONE_PUBLISH_TIME_WINDOW = 1 h | ||
20 | DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 | ||
21 | |||
22 | [namestore] | ||
23 | #PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/ns_log | ||
24 | |||
25 | [zonemaster] | ||
26 | IMMEDIATE_START = YES | ||
27 | START_ON_DEMAND = YES | ||
28 | |||
29 | [rest] | ||
30 | BASIC_AUTH_ENABLED=NO | ||
31 | |||
32 | [revocation] | ||
33 | WORKBITS = 2 | ||
34 | EPOCH_DURATION = 365 d | ||
35 | |||
36 | [dhtcache] | ||
37 | QUOTA = 1 MB | ||
38 | DATABASE = heap | ||
39 | |||
40 | [topology] | ||
41 | TARGET-CONNECTION-COUNT = 16 | ||
42 | AUTOCONNECT = YES | ||
43 | FRIENDS-ONLY = NO | ||
44 | MINIMUM-FRIENDS = 0 | ||
45 | |||
46 | [ats] | ||
47 | WAN_QUOTA_IN = 1 GB | ||
48 | WAN_QUOTA_OUT = 1 GB | ||
49 | |||
50 | [transport] | ||
51 | plugins = tcp | ||
52 | NEIGHBOUR_LIMIT = 50 | ||
53 | PORT = 2091 | ||
54 | |||
55 | [transport-tcp] | ||
56 | TIMEOUT = 300 s | ||
57 | |||
58 | [nat] | ||
59 | DISABLEV6 = YES | ||
60 | BINDTO = 127.0.0.1 | ||
61 | ENABLE_UPNP = NO | ||
62 | BEHIND_NAT = NO | ||
63 | ALLOW_NAT = NO | ||
64 | INTERNAL_ADDRESS = 127.0.0.1 | ||
65 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
diff --git a/src/cli/gns/test_gns_lookup.sh b/src/cli/gns/test_gns_lookup.sh new file mode 100755 index 000000000..92dfae28b --- /dev/null +++ b/src/cli/gns/test_gns_lookup.sh | |||
@@ -0,0 +1,37 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -s PATHS -o GNUNET_HOME -f` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | TEST_IP="127.0.0.1" | ||
20 | MY_EGO="myego" | ||
21 | LABEL="www" | ||
22 | gnunet-arm -s -c test_gns_lookup.conf | ||
23 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
24 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
25 | sleep 0.5 | ||
26 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
27 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
28 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
29 | gnunet-arm -e -c test_gns_lookup.conf | ||
30 | |||
31 | if [ "$RES_IP" = "$TEST_IP" ] | ||
32 | then | ||
33 | exit 0 | ||
34 | else | ||
35 | echo "FAIL: Failed to resolve to proper IP, got $RES_IP." | ||
36 | exit 1 | ||
37 | fi | ||
diff --git a/src/cli/gns/test_gns_lookup_peer1.conf b/src/cli/gns/test_gns_lookup_peer1.conf new file mode 100644 index 000000000..69e2f0973 --- /dev/null +++ b/src/cli/gns/test_gns_lookup_peer1.conf | |||
@@ -0,0 +1,75 @@ | |||
1 | @INLINE@ test_gns_defaults.conf | ||
2 | |||
3 | [namecache] | ||
4 | DISABLE = YES | ||
5 | |||
6 | [PATHS] | ||
7 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-peer-1/ | ||
8 | GNUNET_RUNTIME_DIR = $GNUNET_TMP/test-gnunet-gns-peer-1-system-runtime/ | ||
9 | GNUNET_USER_RUNTIME_DIR = $GNUNET_TMP/test-gnunet-gns-peer-1-user-runtime/ | ||
10 | |||
11 | [dht] | ||
12 | START_ON_DEMAND = YES | ||
13 | IMMEDIATE_START = YES | ||
14 | |||
15 | [gns] | ||
16 | # PREFIX = valgrind --leak-check=full --track-origins=yes | ||
17 | START_ON_DEMAND = YES | ||
18 | AUTO_IMPORT_PKEY = YES | ||
19 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
20 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
21 | RECORD_PUT_INTERVAL = 1 h | ||
22 | ZONE_PUBLISH_TIME_WINDOW = 1 h | ||
23 | DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 | ||
24 | |||
25 | [namestore] | ||
26 | IMMEDIATE_START = YES | ||
27 | #PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/ns_log | ||
28 | |||
29 | [revocation] | ||
30 | WORKBITS = 1 | ||
31 | |||
32 | [dhtcache] | ||
33 | QUOTA = 1 MB | ||
34 | DATABASE = heap | ||
35 | |||
36 | [topology] | ||
37 | TARGET-CONNECTION-COUNT = 16 | ||
38 | AUTOCONNECT = YES | ||
39 | FRIENDS-ONLY = NO | ||
40 | MINIMUM-FRIENDS = 0 | ||
41 | |||
42 | [ats] | ||
43 | WAN_QUOTA_IN = 1 GB | ||
44 | WAN_QUOTA_OUT = 1 GB | ||
45 | |||
46 | [transport] | ||
47 | plugins = unix | ||
48 | NEIGHBOUR_LIMIT = 50 | ||
49 | PORT = 2091 | ||
50 | |||
51 | [transport-unix] | ||
52 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-transport-plugin-unix1.sock | ||
53 | |||
54 | [hostlist] | ||
55 | SERVERS = http://localhost:9999/ | ||
56 | OPTIONS = -b | ||
57 | IMMEDIATE_START = YES | ||
58 | |||
59 | [nat] | ||
60 | DISABLEV6 = YES | ||
61 | BINDTO = 127.0.0.1 | ||
62 | ENABLE_UPNP = NO | ||
63 | BEHIND_NAT = NO | ||
64 | ALLOW_NAT = NO | ||
65 | INTERNAL_ADDRESS = 127.0.0.1 | ||
66 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
67 | |||
68 | [dns2gns] | ||
69 | BINARY = gnunet-dns2gns | ||
70 | START_ON_DEMAND = YES | ||
71 | IMMEDIATE_START = YES | ||
72 | RUN_PER_USER = YES | ||
73 | BIND_TO = 127.0.0.1 | ||
74 | BIND_TO6 = ::1 | ||
75 | OPTIONS = -d 1.1.1.1 -p 12000 | ||
diff --git a/src/cli/gns/test_gns_lookup_peer2.conf b/src/cli/gns/test_gns_lookup_peer2.conf new file mode 100644 index 000000000..3de81d7f3 --- /dev/null +++ b/src/cli/gns/test_gns_lookup_peer2.conf | |||
@@ -0,0 +1,72 @@ | |||
1 | @INLINE@ test_gns_defaults.conf | ||
2 | |||
3 | [namecache] | ||
4 | DISABLE = YES | ||
5 | |||
6 | [PATHS] | ||
7 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-peer-2/ | ||
8 | GNUNET_RUNTIME_DIR = $GNUNET_TMP/test-gnunet-gns-peer-2-runtime/ | ||
9 | GNUNET_USER_RUNTIME_DIR = $GNUNET_TMP/test-gnunet-gns-peer-2-user-runtime/ | ||
10 | |||
11 | [dht] | ||
12 | START_ON_DEMAND = YES | ||
13 | IMMEDIATE_START = YES | ||
14 | |||
15 | [identity] | ||
16 | START_ON_DEMAND = YES | ||
17 | IMMEDIATE_START = YES | ||
18 | |||
19 | [gns] | ||
20 | # PREFIX = valgrind --leak-check=full --track-origins=yes | ||
21 | IMMEDIATE_START = YES | ||
22 | START_ON_DEMAND = YES | ||
23 | AUTO_IMPORT_PKEY = YES | ||
24 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
25 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
26 | RECORD_PUT_INTERVAL = 1 h | ||
27 | ZONE_PUBLISH_TIME_WINDOW = 1 h | ||
28 | DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 | ||
29 | |||
30 | [namestore] | ||
31 | IMMEDIATE_START = YES | ||
32 | #PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/ns_log | ||
33 | |||
34 | [revocation] | ||
35 | WORKBITS = 1 | ||
36 | |||
37 | [dhtcache] | ||
38 | QUOTA = 1 MB | ||
39 | DATABASE = heap | ||
40 | |||
41 | [topology] | ||
42 | TARGET-CONNECTION-COUNT = 16 | ||
43 | AUTOCONNECT = YES | ||
44 | FRIENDS-ONLY = NO | ||
45 | MINIMUM-FRIENDS = 0 | ||
46 | |||
47 | [hostlist] | ||
48 | SERVERS = | ||
49 | HTTPPORT = 9999 | ||
50 | OPTIONS = -p | ||
51 | IMMEDIATE_START = YES | ||
52 | |||
53 | |||
54 | [ats] | ||
55 | WAN_QUOTA_IN = 1 GB | ||
56 | WAN_QUOTA_OUT = 1 GB | ||
57 | |||
58 | [transport] | ||
59 | plugins = unix | ||
60 | NEIGHBOUR_LIMIT = 50 | ||
61 | |||
62 | [transport-unix] | ||
63 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-transport-plugin-unix2.sock | ||
64 | |||
65 | [nat] | ||
66 | DISABLEV6 = YES | ||
67 | BINDTO = 127.0.0.1 | ||
68 | ENABLE_UPNP = NO | ||
69 | BEHIND_NAT = NO | ||
70 | ALLOW_NAT = NO | ||
71 | INTERNAL_ADDRESS = 127.0.0.1 | ||
72 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
diff --git a/src/cli/gns/test_gns_multiple_record_lookup.sh b/src/cli/gns/test_gns_multiple_record_lookup.sh new file mode 100755 index 000000000..52a487329 --- /dev/null +++ b/src/cli/gns/test_gns_multiple_record_lookup.sh | |||
@@ -0,0 +1,95 @@ | |||
1 | #!/bin/bash | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup_peer1.conf" INT | ||
4 | trap "gnunet-arm -e -c test_gns_lookup_peer2.conf" INT | ||
5 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
6 | |||
7 | unset XDG_DATA_HOME | ||
8 | unset XDG_CONFIG_HOME | ||
9 | unset XDG_CACHE_HOME | ||
10 | |||
11 | LOCATION=$(which gnunet-config) | ||
12 | if [ -z $LOCATION ] | ||
13 | then | ||
14 | LOCATION="gnunet-config" | ||
15 | fi | ||
16 | $LOCATION --version 1> /dev/null | ||
17 | if test $? != 0 | ||
18 | then | ||
19 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
20 | exit 77 | ||
21 | fi | ||
22 | |||
23 | rm -rf `gnunet-config -c test_gns_lookup_peer1.conf -f -s paths -o GNUNET_TEST_HOME` | ||
24 | rm -rf `gnunet-config -c test_gns_lookup_peer2.conf -f -s paths -o GNUNET_TEST_HOME` | ||
25 | OTHER_EGO="remoteego" | ||
26 | |||
27 | TEST_IP="127.0.0.1" | ||
28 | TEST_IPV6="dead::beef" | ||
29 | LABEL="fnord" | ||
30 | |||
31 | gnunet-arm -s -c test_gns_lookup_peer2.conf | ||
32 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup_peer2.conf | ||
33 | PKEY=`$DO_TIMEOUT gnunet-identity -d -c test_gns_lookup_peer2.conf | grep $OTHER_EGO | awk '{print $3}'` | ||
34 | |||
35 | # Note: if zonemaster is kept running, it MAY publish the "A" record in the | ||
36 | # DHT immediately and then _LATER_ also the "AAAA" record. But as then there | ||
37 | # will be TWO valid blocks in the DHT (one with only A and one with A and | ||
38 | # AAAA), the subsequent GET for both may fail and only return the result with | ||
39 | # just the "A" record). | ||
40 | # If we _waited_ until the original block with just "A" expired, everything | ||
41 | # would be fine, but we don't want to do that for the test, so we | ||
42 | # simply pause publishing to the DHT until all records are defined. | ||
43 | # In the future, it would be good to have an enhanced gnunet-namestore command | ||
44 | # that would read a series of changes to be made to a record set from | ||
45 | # stdin and do them _all_ *atomically*. Then we would not need to do this. | ||
46 | |||
47 | gnunet-arm -c test_gns_lookup_peer2.conf -k zonemaster | ||
48 | |||
49 | gnunet-namestore -p -z $OTHER_EGO -a -n $LABEL -t A -V $TEST_IP -e 3600s -c test_gns_lookup_peer2.conf | ||
50 | gnunet-namestore -p -z $OTHER_EGO -a -n $LABEL -t AAAA -V $TEST_IPV6 -e 3600s -c test_gns_lookup_peer2.conf | ||
51 | gnunet-namestore -D -z $OTHER_EGO -n $LABEL -c test_gns_lookup_peer2.conf | ||
52 | |||
53 | gnunet-arm -c test_gns_lookup_peer2.conf -i zonemaster | ||
54 | |||
55 | gnunet-arm -s -c test_gns_lookup_peer1.conf | ||
56 | |||
57 | |||
58 | RESP=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$PKEY -t ANY -c test_gns_lookup_peer1.conf` | ||
59 | RESP1=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$PKEY -t A -c test_gns_lookup_peer1.conf` | ||
60 | RESP2=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$PKEY -t AAAA -c test_gns_lookup_peer1.conf` | ||
61 | |||
62 | echo "$LABEL.$PKEY" | ||
63 | echo $RESP $RESP1 $RESP2 | ||
64 | |||
65 | gnunet-arm -e -c test_gns_lookup_peer1.conf | ||
66 | gnunet-arm -e -c test_gns_lookup_peer2.conf | ||
67 | |||
68 | gnunet-config -c test_gns_lookup_peer1.conf -f -s paths -o GNUNET_TEST_HOME | ||
69 | |||
70 | rm -rf `gnunet-config -c test_gns_lookup_peer1.conf -f -s paths -o GNUNET_TEST_HOME` | ||
71 | rm -rf `gnunet-config -c test_gns_lookup_peer2.conf -f -s paths -o GNUNET_TEST_HOME` | ||
72 | |||
73 | RESPONSES=($(echo $RESP | tr "\n" " " )) | ||
74 | |||
75 | if [ "$RESP1" == "$TEST_IP" ] | ||
76 | then | ||
77 | echo "PASS: A record resolution from DHT via separate peer" | ||
78 | else | ||
79 | echo "FAIL: A record resolution from DHT via separate peer, got $RESP1, expected $TEST_IP" | ||
80 | exit 1 | ||
81 | fi | ||
82 | if [ "$RESP2" == "$TEST_IPV6" ] | ||
83 | then | ||
84 | echo "PASS: AAAA record resolution from DHT via separate peer" | ||
85 | else | ||
86 | echo "FAIL: AAAA record resolution from DHT via separate peer, got $RESP2, expected $TEST_IPV6" | ||
87 | exit 1 | ||
88 | fi | ||
89 | if [[ "${RESPONSES[0]} ${RESPONSES[1]}" == "$TEST_IPV6 $TEST_IP" ]] || [[ "${RESPONSES[0]} ${RESPONSES[1]}" == "$TEST_IP $TEST_IPV6" ]] | ||
90 | then | ||
91 | echo "PASS: ANY record resolution from DHT via separate peer" | ||
92 | else | ||
93 | echo "FAIL: ANY record resolution from DHT via separate peer, got $RESP, expected $TEST_IPV6 $TEST_IP or $TEST_IP $TEST_IPV6" | ||
94 | exit 1 | ||
95 | fi | ||
diff --git a/src/cli/gns/test_gns_mx_lookup.sh b/src/cli/gns/test_gns_mx_lookup.sh new file mode 100755 index 000000000..6f2b8192d --- /dev/null +++ b/src/cli/gns/test_gns_mx_lookup.sh | |||
@@ -0,0 +1,44 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.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 | TEST_MX="5 mail.+" | ||
22 | gnunet-arm -s -c test_gns_lookup.conf | ||
23 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
24 | PKEY=`gnunet-identity -d | grep "$MY_EGO - " | awk '{print $3'}` | ||
25 | WANT_MX="5 mail.$PKEY" | ||
26 | gnunet-namestore -p -z $MY_EGO -a -n www -t MX -V "$TEST_MX" -e never -c test_gns_lookup.conf | ||
27 | sleep 0.5 | ||
28 | RES_MX=`$DO_TIMEOUT gnunet-gns --raw -u www.$MY_EGO -t MX -c test_gns_lookup.conf` | ||
29 | gnunet-namestore -z $MY_EGO -d -n www -t MX -V "$TEST_MX" -e never -c test_gns_lookup.conf | ||
30 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
31 | gnunet-arm -e -c test_gns_lookup.conf | ||
32 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
33 | |||
34 | # make cmp case-insensitive by converting to lower case first | ||
35 | RES_MX=`echo $RES_MX | tr [A-Z] [a-z]` | ||
36 | WANT_MX=`echo $WANT_MX | tr [A-Z] [a-z]` | ||
37 | |||
38 | if [ "$RES_MX" = "$WANT_MX" ] | ||
39 | then | ||
40 | exit 0 | ||
41 | else | ||
42 | echo "FAIL: did not get proper IP, got $RES_MX, expected $WANT_MX." | ||
43 | exit 1 | ||
44 | fi | ||
diff --git a/src/cli/gns/test_gns_quickupdate.sh b/src/cli/gns/test_gns_quickupdate.sh new file mode 100755 index 000000000..eac69103d --- /dev/null +++ b/src/cli/gns/test_gns_quickupdate.sh | |||
@@ -0,0 +1,65 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | MY_EGO="myego" | ||
18 | OTHER_EGO="delegatedego" | ||
19 | |||
20 | |||
21 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
22 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
23 | TEST_IP="127.0.0.1" | ||
24 | gnunet-arm -s -c test_gns_lookup.conf | ||
25 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
26 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup.conf | ||
27 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep $OTHER_EGO | awk '{print $3}') | ||
28 | gnunet-arm -i gns -c test_gns_lookup.conf | ||
29 | gnunet-namestore -p -z $MY_EGO -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
30 | # Give GNS/namestore time to fully start and finish initial iteration | ||
31 | sleep 2 | ||
32 | # Performing namestore update | ||
33 | gnunet-namestore -p -z $OTHER_EGO -a -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
34 | # Give GNS chance to observe store event via monitor | ||
35 | sleep 1 | ||
36 | gnunet-namestore -z $OTHER_EGO -d -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
37 | # give GNS chance to process monitor event | ||
38 | sleep 1 | ||
39 | # stop everything and restart to check that DHT PUT did happen | ||
40 | gnunet-arm -k gns -c test_gns_lookup.conf | ||
41 | gnunet-arm -k namestore -c test_gns_lookup.conf | ||
42 | gnunet-arm -k namecache -c test_gns_lookup.conf | ||
43 | gnunet-arm -k zonemaster -c test_gns_lookup.conf | ||
44 | # Purge nameacache, as we might otherwise fetch from there | ||
45 | # FIXME: testcase started failing after the line below was fixed by adding '-f', | ||
46 | # might have never worked (!) | ||
47 | rm -r `gnunet-config -f -c test_gns_lookup.conf -s namecache-sqlite -o FILENAME` | ||
48 | gnunet-arm -i namestore -c test_gns_lookup.conf | ||
49 | gnunet-arm -i namecache -c test_gns_lookup.conf | ||
50 | gnunet-arm -i zonemaster -c test_gns_lookup.conf | ||
51 | gnunet-arm -i gns -c test_gns_lookup.conf | ||
52 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
53 | gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
54 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
55 | gnunet-identity -D $OTHER_EGO -c test_gns_lookup.conf | ||
56 | gnunet-arm -e -c test_gns_lookup.conf | ||
57 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
58 | |||
59 | if [ "$RES_IP" = "$TEST_IP" ] | ||
60 | then | ||
61 | exit 0 | ||
62 | else | ||
63 | echo "Failed to properly resolve IP, expected $TEST_IP, got $RES_IP." | ||
64 | exit 1 | ||
65 | fi | ||
diff --git a/src/cli/gns/test_gns_redirect_lookup.sh b/src/cli/gns/test_gns_redirect_lookup.sh new file mode 100755 index 000000000..90729713d --- /dev/null +++ b/src/cli/gns/test_gns_redirect_lookup.sh | |||
@@ -0,0 +1,100 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | # permissive DNS resolver we will use for the test | ||
18 | DNS_RESOLVER="8.8.8.8" | ||
19 | if ! nslookup gnunet.org $DNS_RESOLVER > /dev/null 2>&1 | ||
20 | then | ||
21 | echo "Cannot reach DNS, skipping test" | ||
22 | exit 77 | ||
23 | fi | ||
24 | |||
25 | |||
26 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
27 | |||
28 | TEST_IP_PLUS="127.0.0.1" | ||
29 | TEST_IP_DNS="147.87.255.218" | ||
30 | TEST_RECORD_REDIRECT_SERVER="server" | ||
31 | TEST_RECORD_REDIRECT_PLUS="server.+" | ||
32 | TEST_RECORD_REDIRECT_DNS="gnunet.org" | ||
33 | TEST_RECORD_NAME_SERVER="server" | ||
34 | TEST_RECORD_NAME_PLUS="www" | ||
35 | TEST_RECORD_NAME_ZKEY="www2" | ||
36 | TEST_RECORD_NAME_DNS="www3" | ||
37 | MY_EGO="myego" | ||
38 | TEST_DOMAIN_PLUS="www.$MY_EGO" | ||
39 | TEST_DOMAIN_ZKEY="www2.$MY_EGO" | ||
40 | TEST_DOMAIN_DNS="www3.$MY_EGO" | ||
41 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
42 | |||
43 | gnunet-arm -s -c test_gns_lookup.conf | ||
44 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
45 | MY_EGO_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep ${MY_EGO} | awk '{print $3}') | ||
46 | TEST_RECORD_REDIRECT_ZKEY="server.${MY_EGO_PKEY}" | ||
47 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME_DNS -t REDIRECT -V $TEST_RECORD_REDIRECT_DNS -e never -c test_gns_lookup.conf | ||
48 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME_PLUS -t REDIRECT -V $TEST_RECORD_REDIRECT_PLUS -e never -c test_gns_lookup.conf | ||
49 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME_ZKEY -t REDIRECT -V $TEST_RECORD_REDIRECT_ZKEY -e never -c test_gns_lookup.conf | ||
50 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_REDIRECT_SERVER -t A -V $TEST_IP_PLUS -e never -c test_gns_lookup.conf | ||
51 | sleep 1 | ||
52 | RES_REDIRECT=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_PLUS -t A -c test_gns_lookup.conf` | ||
53 | RES_REDIRECT_RAW=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_PLUS -t REDIRECT -c test_gns_lookup.conf` | ||
54 | RES_REDIRECT_ZKEY=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_ZKEY -t A -c test_gns_lookup.conf` | ||
55 | RES_REDIRECT_DNS=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN_DNS -t A -c test_gns_lookup.conf | grep $TEST_IP_DNS` | ||
56 | |||
57 | TESTEGOZONE=`gnunet-identity -c test_gns_lookup.conf -d | awk '{print $3}'` | ||
58 | gnunet-namestore -p -z $MY_EGO -d -n $TEST_RECORD_NAME_DNS -t REDIRECT -V $TEST_RECORD_REDIRECT_DNS -e never -c test_gns_lookup.conf | ||
59 | gnunet-namestore -p -z $MY_EGO -d -n $TEST_RECORD_NAME_PLUS -t REDIRECT -V $TEST_RECORD_REDIRECT_PLUS -e never -c test_gns_lookup.conf | ||
60 | gnunet-namestore -p -z $MY_EGO -d -n $TEST_RECORD_NAME_ZKEY -t REDIRECT -V $TEST_RECORD_REDIRECT_ZKEY -e never -c test_gns_lookup.conf | ||
61 | gnunet-namestore -p -z $MY_EGO -d -n $TEST_RECORD_REDIRECT_SERVER -t A -V $TEST_IP_PLUS -e never -c test_gns_lookup.conf | ||
62 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
63 | gnunet-arm -e -c test_gns_lookup.conf | ||
64 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
65 | |||
66 | # make cmp case-insensitive by converting to lower case first | ||
67 | RES_REDIRECT_RAW=`echo $RES_REDIRECT_RAW | tr [A-Z] [a-z]` | ||
68 | TESTEGOZONE=`echo $TESTEGOZONE | tr [A-Z] [a-z]` | ||
69 | if [ "$RES_REDIRECT_RAW" = "server.$TESTEGOZONE" ] | ||
70 | then | ||
71 | echo "PASS: REDIRECT resolution from GNS" | ||
72 | else | ||
73 | echo "FAIL: REDIRECT resolution from GNS, got $RES_REDIRECT_RAW, expected server.$TESTEGOZONE." | ||
74 | exit 1 | ||
75 | fi | ||
76 | |||
77 | if [ "$RES_REDIRECT" = "$TEST_IP_PLUS" ] | ||
78 | then | ||
79 | echo "PASS: IP resolution from GNS (.+)" | ||
80 | else | ||
81 | echo "FAIL: IP resolution from GNS (.+), got $RES_REDIRECT, expected $TEST_IP_PLUS." | ||
82 | exit 1 | ||
83 | fi | ||
84 | |||
85 | if [ "$RES_REDIRECT_ZKEY" = "$TEST_IP_PLUS" ] | ||
86 | then | ||
87 | echo "PASS: IP resolution from GNS (.zkey)" | ||
88 | else | ||
89 | echo "FAIL: IP resolution from GNS (.zkey), got $RES_REDIRECT, expected $TEST_IP_PLUS." | ||
90 | exit 1 | ||
91 | fi | ||
92 | |||
93 | if echo "$RES_REDIRECT_DNS" | grep "$TEST_IP_DNS" > /dev/null | ||
94 | then | ||
95 | echo "PASS: IP resolution from DNS" | ||
96 | exit 0 | ||
97 | else | ||
98 | echo "FAIL: IP resolution from DNS, got $RES_REDIRECT_DNS, expected $TEST_IP_DNS." | ||
99 | exit 1 | ||
100 | fi | ||
diff --git a/src/cli/gns/test_gns_rel_expiration.sh b/src/cli/gns/test_gns_rel_expiration.sh new file mode 100755 index 000000000..a240cfd0f --- /dev/null +++ b/src/cli/gns/test_gns_rel_expiration.sh | |||
@@ -0,0 +1,64 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
4 | |||
5 | LOCATION=$(which gnunet-config) | ||
6 | if [ -z $LOCATION ] | ||
7 | then | ||
8 | LOCATION="gnunet-config" | ||
9 | fi | ||
10 | |||
11 | if [ -z $(which timeout) ] | ||
12 | then | ||
13 | echo "timeout utility not found which is required for test." | ||
14 | exit 77 | ||
15 | fi | ||
16 | |||
17 | $LOCATION --version 1> /dev/null | ||
18 | if test $? != 0 | ||
19 | then | ||
20 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
21 | exit 77 | ||
22 | fi | ||
23 | |||
24 | MY_EGO="myego" | ||
25 | OTHER_EGO="delegatedego" | ||
26 | |||
27 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
28 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
29 | TEST_IP="127.0.0.1" | ||
30 | gnunet-arm -s -c test_gns_lookup.conf | ||
31 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
32 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup.conf | ||
33 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep $OTHER_EGO | awk '{print $3}') | ||
34 | gnunet-namestore -p -z $MY_EGO -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
35 | gnunet-namestore -p -z $OTHER_EGO -a -n www -t A -V $TEST_IP -e '5 s' -c test_gns_lookup.conf | ||
36 | gnunet-arm -i gns -c test_gns_lookup.conf | ||
37 | sleep 0.5 | ||
38 | # confirm that lookup currently works | ||
39 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
40 | # remove entry | ||
41 | gnunet-namestore -z $OTHER_EGO -d -n www -t A -V $TEST_IP -e '5 s' -c test_gns_lookup.conf | ||
42 | # wait for old entry with 5s 'expiration' to definitively expire | ||
43 | sleep 6 | ||
44 | # try again, should no longer work | ||
45 | RES_IP_EXP=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
46 | gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
47 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
48 | gnunet-identity -D $OTHER_EGO -c test_gns_lookup.conf | ||
49 | gnunet-arm -e -c test_gns_lookup.conf | ||
50 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
51 | |||
52 | if [ "$RES_IP_EXP" = "$TEST_IP" ] | ||
53 | then | ||
54 | echo "Failed to properly expire IP, got $RES_IP_EXP." | ||
55 | exit 1 | ||
56 | fi | ||
57 | |||
58 | if [ "$RES_IP" = "$TEST_IP" ] | ||
59 | then | ||
60 | exit 0 | ||
61 | else | ||
62 | echo "Failed to properly resolve IP, got $RES_IP." | ||
63 | exit 1 | ||
64 | fi | ||
diff --git a/src/cli/gns/test_gns_revocation.sh b/src/cli/gns/test_gns_revocation.sh new file mode 100755 index 000000000..2253adcb4 --- /dev/null +++ b/src/cli/gns/test_gns_revocation.sh | |||
@@ -0,0 +1,50 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
4 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
5 | |||
6 | LOCATION=$(which gnunet-config) | ||
7 | if [ -z $LOCATION ] | ||
8 | then | ||
9 | LOCATION="gnunet-config" | ||
10 | fi | ||
11 | $LOCATION --version 1> /dev/null | ||
12 | if test $? != 0 | ||
13 | then | ||
14 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
15 | exit 77 | ||
16 | fi | ||
17 | |||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
19 | MY_EGO="myego" | ||
20 | OTHER_EGO="delegatedego" | ||
21 | TEST_IP="127.0.0.1" | ||
22 | |||
23 | gnunet-arm -s -c test_gns_lookup.conf | ||
24 | gnunet-identity -C $OTHER_EGO -c test_gns_lookup.conf | ||
25 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep $OTHER_EGO | awk '{print $3}') | ||
26 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
27 | gnunet-namestore -p -z $MY_EGO -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
28 | gnunet-namestore -p -z $OTHER_EGO -a -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
29 | sleep 1 | ||
30 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
31 | gnunet-revocation -R $OTHER_EGO -p -c test_gns_lookup.conf | ||
32 | RES_IP_REV=`$DO_TIMEOUT gnunet-gns --raw -u www.b.$MY_EGO -t A -c test_gns_lookup.conf` | ||
33 | gnunet-namestore -z $MY_EGO -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
34 | gnunet-namestore -z $OTHER_EGO -d -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
35 | gnunet-arm -e -c test_gns_lookup.conf | ||
36 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
37 | |||
38 | if [ "$RES_IP" != "$TEST_IP" ] | ||
39 | then | ||
40 | echo "Failed to resolve to proper IP, got $RES_IP." | ||
41 | exit 1 | ||
42 | fi | ||
43 | |||
44 | if [ "x$RES_IP_REV" = "x" ] | ||
45 | then | ||
46 | exit 0 | ||
47 | else | ||
48 | echo "Failed to revoke zone, got $RES_IP_REV." | ||
49 | exit 1 | ||
50 | fi | ||
diff --git a/src/cli/gns/test_gns_sbox.sh b/src/cli/gns/test_gns_sbox.sh new file mode 100755 index 000000000..6918bf130 --- /dev/null +++ b/src/cli/gns/test_gns_sbox.sh | |||
@@ -0,0 +1,121 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 7" | ||
19 | TEST_A="139.134.54.9" | ||
20 | MY_EGO="myego" | ||
21 | LABEL="testsbox" | ||
22 | PREFIX1="_name" | ||
23 | PREFIX2="__" | ||
24 | PREFIX3="_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_" | ||
25 | PREFIX4="abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz._abc" | ||
26 | PREFIX5="abc.abc._abc.abc" | ||
27 | PREFIX6="abc.abc._abc.abc._abc" | ||
28 | PREFIX7="abc.abc._abc.abc._abc.abc" | ||
29 | PREFIX8="_at" | ||
30 | gnunet-arm -s -c test_gns_lookup.conf | ||
31 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
32 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX1 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
33 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX2 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
34 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX3 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
35 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX4 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
36 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX5 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
37 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX6 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
38 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t SBOX -V "$PREFIX7 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
39 | gnunet-namestore -p -z $MY_EGO -a -n '@' -t SBOX -V "$PREFIX8 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
40 | sleep 0.5 | ||
41 | RES_A1=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX1.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
42 | RES_A2=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX2.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
43 | RES_A3=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX3.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
44 | RES_A4=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX4.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
45 | RES_A5=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX5.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
46 | RES_A6=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX6.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
47 | RES_A7=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX7.$LABEL.$MY_EGO -t A -c test_gns_lookup.conf` | ||
48 | RES_A8=`$DO_TIMEOUT gnunet-gns --raw -u $PREFIX8.$MY_EGO -t A -c test_gns_lookup.conf` | ||
49 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$PREFIX1 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
50 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$PREFIX2 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
51 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$PREFIX3 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
52 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$PREFIX4 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
53 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t SBOX -V "$PREFIX6 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
54 | gnunet-namestore -z $MY_EGO -d -n '@' -t SBOX -V "$PREFIX8 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
55 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
56 | gnunet-arm -e -c test_gns_lookup.conf | ||
57 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
58 | |||
59 | if [ "$RES_A1" = "$TEST_A" ] | ||
60 | then | ||
61 | exit 0 | ||
62 | else | ||
63 | echo "Failed to resolve to proper A, got '$RES_A1'." | ||
64 | exit 1 | ||
65 | fi | ||
66 | |||
67 | if [ "$RES_A2" = "$TEST_A" ] | ||
68 | then | ||
69 | exit 0 | ||
70 | else | ||
71 | echo "Failed to resolve to proper A, got '$RES_A2'." | ||
72 | exit 1 | ||
73 | fi | ||
74 | |||
75 | if [ "$RES_A3" = "$TEST_A" ] | ||
76 | then | ||
77 | exit 0 | ||
78 | else | ||
79 | echo "Failed to resolve to proper A, got '$RES_A3'." | ||
80 | exit 1 | ||
81 | fi | ||
82 | |||
83 | if [ "$RES_A4" = "$TEST_A" ] | ||
84 | then | ||
85 | exit 0 | ||
86 | else | ||
87 | echo "Failed to resolve to proper A, got '$RES_A4'." | ||
88 | exit 1 | ||
89 | fi | ||
90 | |||
91 | if [ "$RES_A5" = "$TEST_A" ] | ||
92 | then | ||
93 | echo "Should have failed to resolve to proper A, got '$RES_A5' anyway." | ||
94 | exit 1 | ||
95 | else | ||
96 | exit 0 | ||
97 | fi | ||
98 | |||
99 | if [ "$RES_A6" = "$TEST_A" ] | ||
100 | then | ||
101 | exit 0 | ||
102 | else | ||
103 | echo "Failed to resolve to proper A, got '$RES_A6'." | ||
104 | exit 1 | ||
105 | fi | ||
106 | |||
107 | if [ "$RES_A7" = "$TEST_A" ] | ||
108 | then | ||
109 | echo "Should have failed to resolve to proper A, got '$RES_A7' anyway." | ||
110 | exit 1 | ||
111 | else | ||
112 | exit 0 | ||
113 | fi | ||
114 | |||
115 | if [ "$RES_A8" = "$TEST_A" ] | ||
116 | then | ||
117 | exit 0 | ||
118 | else | ||
119 | echo "Failed to resolve to proper A, got '$RES_A8'." | ||
120 | exit 1 | ||
121 | fi | ||
diff --git a/src/cli/gns/test_gns_sbox_simple.sh b/src/cli/gns/test_gns_sbox_simple.sh new file mode 100755 index 000000000..f0d31e471 --- /dev/null +++ b/src/cli/gns/test_gns_sbox_simple.sh | |||
@@ -0,0 +1,39 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | TEST_A="139.134.54.9" | ||
20 | MY_EGO="myego" | ||
21 | HASH="c93f1e400f26708f98cb19d936620da35eec8f72e57f9eec01c1afd6" | ||
22 | PROTOCOL_TEXT="_smimecert" | ||
23 | gnunet-arm -s -c test_gns_lookup.conf | ||
24 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
25 | gnunet-namestore -p -z $MY_EGO -a -n '@' -t SBOX -V "$HASH.$PROTOCOL_TEXT 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
26 | sleep 0.5 | ||
27 | RES_A=`$DO_TIMEOUT gnunet-gns --raw -u $HASH.$PROTOCOL_TEXT.$MY_EGO -t A -c test_gns_lookup.conf` | ||
28 | gnunet-namestore -z $MY_EGO -d -n '@' -t SBOX -V "$HASH.$PROTOCOL_TEXT 1 $TEST_A" -e never -c test_gns_lookup.conf | ||
29 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
30 | gnunet-arm -e -c test_gns_lookup.conf | ||
31 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
32 | |||
33 | if [ "$RES_A" = "$TEST_A" ] | ||
34 | then | ||
35 | exit 0 | ||
36 | else | ||
37 | echo "Failed to resolve to proper A, got '$RES_A'." | ||
38 | exit 1 | ||
39 | fi | ||
diff --git a/src/cli/gns/test_gns_simple_lookup.conf b/src/cli/gns/test_gns_simple_lookup.conf new file mode 100644 index 000000000..374731377 --- /dev/null +++ b/src/cli/gns/test_gns_simple_lookup.conf | |||
@@ -0,0 +1,97 @@ | |||
1 | @INLINE@ test_gns_defaults.conf | ||
2 | [fs] | ||
3 | START_ON_DEMAND = NO | ||
4 | |||
5 | [resolver] | ||
6 | START_ON_DEMAND = YES | ||
7 | HOSTNAME = localhost | ||
8 | |||
9 | [dht] | ||
10 | START_ON_DEMAND = YES | ||
11 | ACCEPT_FROM6 = ::1; | ||
12 | ACCEPT_FROM = 127.0.0.1; | ||
13 | HOSTNAME = localhost | ||
14 | PORT = 12100 | ||
15 | BINARY = gnunet-service-dht | ||
16 | |||
17 | [dhtcache] | ||
18 | QUOTA = 1 MB | ||
19 | DATABASE = heap | ||
20 | |||
21 | [transport] | ||
22 | PLUGINS = tcp | ||
23 | ACCEPT_FROM6 = ::1; | ||
24 | ACCEPT_FROM = 127.0.0.1; | ||
25 | NEIGHBOUR_LIMIT = 50 | ||
26 | PORT = 12365 | ||
27 | |||
28 | [ats] | ||
29 | WAN_QUOTA_IN = 1 GB | ||
30 | WAN_QUOTA_OUT = 1 GB | ||
31 | |||
32 | [core] | ||
33 | PORT = 12092 | ||
34 | |||
35 | [arm] | ||
36 | PORT = 12366 | ||
37 | |||
38 | [transport-tcp] | ||
39 | TIMEOUT = 300 s | ||
40 | PORT = 12368 | ||
41 | BINDTO = 127.0.0.1 | ||
42 | |||
43 | [PATHS] | ||
44 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-gns-peer-1/ | ||
45 | |||
46 | |||
47 | [nat] | ||
48 | DISABLEV6 = YES | ||
49 | ENABLE_UPNP = NO | ||
50 | BEHIND_NAT = NO | ||
51 | ALLOW_NAT = NO | ||
52 | INTERNAL_ADDRESS = 127.0.0.1 | ||
53 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
54 | USE_LOCALADDR = NO | ||
55 | |||
56 | [dns] | ||
57 | START_ON_DEMAND = YES | ||
58 | DNS_EXIT = 8.8.8.8 | ||
59 | |||
60 | [gns] | ||
61 | #PREFIX = valgrind --leak-check=full --track-origins=yes | ||
62 | START_ON_DEMAND = YES | ||
63 | BINARY = gnunet-service-gns | ||
64 | ZONEKEY = zonefiles/test_zonekey | ||
65 | PRIVATE_ZONE = private | ||
66 | PRIVATE_ZONEKEY = zonefiles/OEFL7A4VEF1B40QLEMTG5D8G1CN6EN16QUSG5R2DT71GRJN34LSG.zkey | ||
67 | SHORTEN_ZONE = short | ||
68 | SHORTEN_ZONEKEY = zonefiles/188JSUMKEF25GVU8TTV0PBNNN8JVCPUEDFV1UHJJU884JD25V0T0.zkey | ||
69 | #ZONEKEY = $GNUNET_TEST_HOME/gns/zonekey.zkey | ||
70 | HIJACK_DNS = NO | ||
71 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-gns.sock | ||
72 | AUTO_IMPORT_PKEY = YES | ||
73 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
74 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
75 | RECORD_PUT_INTERVAL = 1 h | ||
76 | |||
77 | [nse] | ||
78 | START_ON_DEMAND = NO | ||
79 | |||
80 | [statistics] | ||
81 | START_ON_DEMAND = NO | ||
82 | |||
83 | [namestore] | ||
84 | PORT = 22371 | ||
85 | START_ON_DEMAND = YES | ||
86 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-namestore-default.sock | ||
87 | UNIX_MATCH_UID = YES | ||
88 | UNIX_MATCH_GID = YES | ||
89 | HOSTNAME = localhost | ||
90 | BINARY = gnunet-service-namestore | ||
91 | ACCEPT_FROM = 127.0.0.1; | ||
92 | ACCEPT_FROM6 = ::1; | ||
93 | DATABASE = sqlite | ||
94 | ZONEFILE_DIRECTORY = $GNUNET_TEST_HOME | ||
95 | |||
96 | [namestore-sqlite] | ||
97 | FILENAME = $GNUNET_TEST_HOME/sqlite-default.db | ||
diff --git a/src/cli/gns/test_gns_soa_lookup.sh b/src/cli/gns/test_gns_soa_lookup.sh new file mode 100755 index 000000000..a697782bb --- /dev/null +++ b/src/cli/gns/test_gns_soa_lookup.sh | |||
@@ -0,0 +1,51 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
18 | |||
19 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
20 | MY_EGO="myego" | ||
21 | TEST_DOMAIN="homepage.$MY_EGO" | ||
22 | # some public DNS resolver we can use | ||
23 | #TEST_IP_GNS2DNS="184.172.157.218" # This one seems currently down. | ||
24 | TEST_IP_GNS2DNS="8.8.8.8" | ||
25 | TEST_RECORD_NAME="homepage" | ||
26 | TEST_RECORD_GNS2DNS="gnunet.org" | ||
27 | |||
28 | if ! nslookup $TEST_RECORD_GNS2DNS $TEST_IP_GNS2DNS > /dev/null 2>&1 | ||
29 | then | ||
30 | echo "Cannot reach DNS, skipping test" | ||
31 | exit 77 | ||
32 | fi | ||
33 | |||
34 | gnunet-arm -s -c test_gns_lookup.conf | ||
35 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
36 | gnunet-namestore -p -z $MY_EGO -a -n $TEST_RECORD_NAME -t GNS2DNS -V ${TEST_RECORD_GNS2DNS}@${TEST_IP_GNS2DNS} -e never -c test_gns_lookup.conf | ||
37 | sleep 0.5 | ||
38 | RES_SOA=`$DO_TIMEOUT gnunet-gns --raw -u $TEST_DOMAIN -t SOA -c test_gns_lookup.conf` | ||
39 | gnunet-namestore -z $MY_EGO -d -n $TEST_RECORD_NAME -t GNS2DNS -V ${TEST_RECORD_GNS2DNS}@${TEST_IP_GNS2DNS} -e never -c test_gns_lookup.conf > /dev/null 2>&1 | ||
40 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
41 | gnunet-arm -e -c test_gns_lookup.conf | ||
42 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
43 | |||
44 | if [ "x$RES_SOA" != "x" ] | ||
45 | then | ||
46 | echo "PASS: Resolved SOA for $TEST_DOMAIN to $RES_SOA." | ||
47 | exit 0 | ||
48 | else | ||
49 | echo "Failed to resolve to proper SOA for $TEST_DOMAIN, got no result." | ||
50 | exit 1 | ||
51 | fi | ||
diff --git a/src/cli/gns/test_gns_txt_lookup.sh b/src/cli/gns/test_gns_txt_lookup.sh new file mode 100755 index 000000000..4e36e8ad8 --- /dev/null +++ b/src/cli/gns/test_gns_txt_lookup.sh | |||
@@ -0,0 +1,38 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.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_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
18 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | TEST_TXT="GNS powered txt record data" | ||
20 | MY_EGO="myego" | ||
21 | LABEL="testtxt" | ||
22 | gnunet-arm -s -c test_gns_lookup.conf | ||
23 | gnunet-identity -C $MY_EGO -c test_gns_lookup.conf | ||
24 | gnunet-namestore -p -z $MY_EGO -a -n $LABEL -t TXT -V "$TEST_TXT" -e never -c test_gns_lookup.conf | ||
25 | sleep 0.5 | ||
26 | RES_TXT=`$DO_TIMEOUT gnunet-gns --raw -u $LABEL.$MY_EGO -t TXT -c test_gns_lookup.conf` | ||
27 | gnunet-namestore -z $MY_EGO -d -n $LABEL -t TXT -V "$TEST_TXT" -e never -c test_gns_lookup.conf | ||
28 | gnunet-identity -D $MY_EGO -c test_gns_lookup.conf | ||
29 | gnunet-arm -e -c test_gns_lookup.conf | ||
30 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
31 | |||
32 | if [ "$RES_TXT" = "$TEST_TXT" ] | ||
33 | then | ||
34 | exit 0 | ||
35 | else | ||
36 | echo "Failed to resolve to proper TXT, got '$RES_TXT'." | ||
37 | exit 1 | ||
38 | fi | ||
diff --git a/src/cli/gns/test_gns_zkey_lookup.sh b/src/cli/gns/test_gns_zkey_lookup.sh new file mode 100755 index 000000000..3d4aefc7c --- /dev/null +++ b/src/cli/gns/test_gns_zkey_lookup.sh | |||
@@ -0,0 +1,39 @@ | |||
1 | #!/bin/sh | ||
2 | # This file is in the public domain. | ||
3 | trap "gnunet-arm -e -c test_gns_lookup.conf" INT | ||
4 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" | ||
5 | |||
6 | LOCATION=$(which gnunet-config) | ||
7 | if [ -z $LOCATION ] | ||
8 | then | ||
9 | LOCATION="gnunet-config" | ||
10 | fi | ||
11 | $LOCATION --version 1> /dev/null | ||
12 | if test $? != 0 | ||
13 | then | ||
14 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
15 | exit 77 | ||
16 | fi | ||
17 | |||
18 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
19 | |||
20 | TEST_IP="127.0.0.1" | ||
21 | gnunet-arm -s -c test_gns_lookup.conf | ||
22 | gnunet-identity -C delegatedego -c test_gns_lookup.conf | ||
23 | DELEGATED_PKEY=$(gnunet-identity -d -c test_gns_lookup.conf | grep delegatedego | awk '{print $3}') | ||
24 | gnunet-identity -C testego -c test_gns_lookup.conf | ||
25 | gnunet-namestore -p -z testego -a -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
26 | gnunet-namestore -p -z delegatedego -a -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
27 | RES_IP=`$DO_TIMEOUT gnunet-gns --raw -u www.${DELEGATED_PKEY} -t A -c test_gns_lookup.conf` | ||
28 | gnunet-namestore -z testego -d -n b -t PKEY -V $DELEGATED_PKEY -e never -c test_gns_lookup.conf | ||
29 | gnunet-namestore -z delegatedego -d -n www -t A -V $TEST_IP -e never -c test_gns_lookup.conf | ||
30 | gnunet-arm -e -c test_gns_lookup.conf | ||
31 | rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME` | ||
32 | |||
33 | if [ "$RES_IP" = "$TEST_IP" ] | ||
34 | then | ||
35 | exit 0 | ||
36 | else | ||
37 | echo "Failed to resolve to proper IP, got $RES_IP, wanted $TEST_IP." | ||
38 | exit 1 | ||
39 | fi | ||
diff --git a/src/cli/gns/test_gnunet_gns.sh.in b/src/cli/gns/test_gnunet_gns.sh.in new file mode 100755 index 000000000..d0c07b4e4 --- /dev/null +++ b/src/cli/gns/test_gnunet_gns.sh.in | |||
@@ -0,0 +1,47 @@ | |||
1 | #!/bin/bash | ||
2 | # This file is in the public domain. | ||
3 | # test -z being correct was a false assumption here. | ||
4 | # I have no executable 'fooble', but this will | ||
5 | # return 1: | ||
6 | # if test -z "`which fooble`"; then echo 1; fi | ||
7 | # The command builtin might not work with busybox's ash | ||
8 | # but this works for now. | ||
9 | dir=$(dirname "$0") | ||
10 | |||
11 | existence() { | ||
12 | command -v "$1" >/dev/null 2>&1 | ||
13 | } | ||
14 | |||
15 | LOCATION=`existence gnunet-config` | ||
16 | if test -z $LOCATION; then | ||
17 | LOCATION="gnunet-config" | ||
18 | fi | ||
19 | $LOCATION --version | ||
20 | if test $? != 0 | ||
21 | then | ||
22 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
23 | exit 77 | ||
24 | fi | ||
25 | |||
26 | trap "gnunet-arm -e -c test_gns_lookup.conf" SIGINT | ||
27 | ME=`whoami` | ||
28 | if [ "$ME" != "root" ] | ||
29 | then | ||
30 | echo "This test only works if run as root. Skipping." | ||
31 | exit 77 | ||
32 | fi | ||
33 | export PATH=".:$PATH" | ||
34 | gnunet-service-gns -c gns.conf & | ||
35 | sleep 1 | ||
36 | LO=`nslookup alice.gnu | grep Address | tail -n1` | ||
37 | if [ "$LO" != "Address: 1.2.3.4" ] | ||
38 | then | ||
39 | echo "Fail: $LO" | ||
40 | fi | ||
41 | LO=`nslookup www.bob.gnu | grep Address | tail -n1` | ||
42 | if [ "$LO" != "Address: 4.5.6.7" ] | ||
43 | then | ||
44 | echo "Fail: $LO" | ||
45 | fi | ||
46 | # XXX: jobs. a builtin by bash, netbsd sh, maybe leave it be for now. | ||
47 | kill `jobs -p` | ||
diff --git a/src/cli/gns/zonefiles/188JSUMKEF25GVU8TTV0PBNNN8JVCPUEDFV1UHJJU884JD25V0T0.zkey b/src/cli/gns/zonefiles/188JSUMKEF25GVU8TTV0PBNNN8JVCPUEDFV1UHJJU884JD25V0T0.zkey new file mode 100644 index 000000000..895946037 --- /dev/null +++ b/src/cli/gns/zonefiles/188JSUMKEF25GVU8TTV0PBNNN8JVCPUEDFV1UHJJU884JD25V0T0.zkey | |||
Binary files differ | |||
diff --git a/src/cli/gns/zonefiles/J7POEUT41A8PBFS7KVVDRF88GBOU4HK8PSU5QKVLVE3R9T91E99G.zkey b/src/cli/gns/zonefiles/J7POEUT41A8PBFS7KVVDRF88GBOU4HK8PSU5QKVLVE3R9T91E99G.zkey new file mode 100644 index 000000000..3ef49f0ac --- /dev/null +++ b/src/cli/gns/zonefiles/J7POEUT41A8PBFS7KVVDRF88GBOU4HK8PSU5QKVLVE3R9T91E99G.zkey | |||
Binary files differ | |||
diff --git a/src/cli/gns/zonefiles/OEFL7A4VEF1B40QLEMTG5D8G1CN6EN16QUSG5R2DT71GRJN34LSG.zkey b/src/cli/gns/zonefiles/OEFL7A4VEF1B40QLEMTG5D8G1CN6EN16QUSG5R2DT71GRJN34LSG.zkey new file mode 100644 index 000000000..89e0b3a0a --- /dev/null +++ b/src/cli/gns/zonefiles/OEFL7A4VEF1B40QLEMTG5D8G1CN6EN16QUSG5R2DT71GRJN34LSG.zkey | |||
Binary files differ | |||
diff --git a/src/cli/gns/zonefiles/test_zonekey b/src/cli/gns/zonefiles/test_zonekey new file mode 100644 index 000000000..870c56315 --- /dev/null +++ b/src/cli/gns/zonefiles/test_zonekey | |||
Binary files differ | |||
diff --git a/src/cli/identity/.gitignore b/src/cli/identity/.gitignore new file mode 100644 index 000000000..1c5862dbb --- /dev/null +++ b/src/cli/identity/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-identity | |||
diff --git a/src/cli/identity/Makefile.am b/src/cli/identity/Makefile.am new file mode 100644 index 000000000..68bcc7a41 --- /dev/null +++ b/src/cli/identity/Makefile.am | |||
@@ -0,0 +1,35 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS = --coverage -O0 | ||
8 | XLIB = -lgcov | ||
9 | endif | ||
10 | |||
11 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
12 | |||
13 | bin_PROGRAMS = \ | ||
14 | gnunet-identity | ||
15 | |||
16 | gnunet_identity_SOURCES = \ | ||
17 | gnunet-identity.c | ||
18 | gnunet_identity_LDADD = \ | ||
19 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
20 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
21 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
22 | $(GN_LIBINTL) | ||
23 | |||
24 | check_SCRIPTS = \ | ||
25 | test_identity_messages.sh | ||
26 | |||
27 | # if ENABLE_TEST_RUN | ||
28 | # AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
29 | # TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
30 | # endif | ||
31 | |||
32 | |||
33 | EXTRA_DIST = \ | ||
34 | test_identity_messages.sh | ||
35 | |||
diff --git a/src/cli/identity/gnunet-identity.c b/src/cli/identity/gnunet-identity.c new file mode 100644 index 000000000..9fe4ccc51 --- /dev/null +++ b/src/cli/identity/gnunet-identity.c | |||
@@ -0,0 +1,624 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2018, 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 identity/gnunet-identity.c | ||
22 | * @brief IDENTITY management command line tool | ||
23 | * @author Christian Grothoff | ||
24 | * | ||
25 | * Todo: | ||
26 | * - add options to get default egos | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | #include "gnunet_identity_service.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Return value from main on timeout. | ||
35 | */ | ||
36 | #define TIMEOUT_STATUS_CODE 40 | ||
37 | |||
38 | /** | ||
39 | * Handle to IDENTITY service. | ||
40 | */ | ||
41 | static struct GNUNET_IDENTITY_Handle *sh; | ||
42 | |||
43 | /** | ||
44 | * Was "list" specified? | ||
45 | */ | ||
46 | static int list; | ||
47 | |||
48 | /** | ||
49 | * Was "monitor" specified? | ||
50 | */ | ||
51 | static int monitor; | ||
52 | |||
53 | /** | ||
54 | * Was "private" specified? | ||
55 | */ | ||
56 | static int private_keys; | ||
57 | |||
58 | /** | ||
59 | * Was "verbose" specified? | ||
60 | */ | ||
61 | static unsigned int verbose; | ||
62 | |||
63 | /** | ||
64 | * Was "quiet" specified? | ||
65 | */ | ||
66 | static int quiet; | ||
67 | |||
68 | /** | ||
69 | * Was "eddsa" specified? | ||
70 | */ | ||
71 | static int type_eddsa; | ||
72 | |||
73 | /** | ||
74 | * -W option | ||
75 | */ | ||
76 | static char *write_msg; | ||
77 | |||
78 | /** | ||
79 | * -R option | ||
80 | */ | ||
81 | static char *read_msg; | ||
82 | |||
83 | /** | ||
84 | * -C option | ||
85 | */ | ||
86 | static char *create_ego; | ||
87 | |||
88 | /** | ||
89 | * -D option | ||
90 | */ | ||
91 | static char *delete_ego; | ||
92 | |||
93 | /** | ||
94 | * -P option | ||
95 | */ | ||
96 | static char *privkey_ego; | ||
97 | |||
98 | /** | ||
99 | * -k option | ||
100 | */ | ||
101 | static char *pubkey_msg; | ||
102 | |||
103 | /** | ||
104 | * -s option. | ||
105 | */ | ||
106 | static char *set_ego; | ||
107 | |||
108 | /** | ||
109 | * Operation handle for set operation. | ||
110 | */ | ||
111 | static struct GNUNET_IDENTITY_Operation *set_op; | ||
112 | |||
113 | /** | ||
114 | * Handle for create operation. | ||
115 | */ | ||
116 | static struct GNUNET_IDENTITY_Operation *create_op; | ||
117 | |||
118 | /** | ||
119 | * Handle for delete operation. | ||
120 | */ | ||
121 | static struct GNUNET_IDENTITY_Operation *delete_op; | ||
122 | |||
123 | /** | ||
124 | * Private key from command line option, or NULL. | ||
125 | */ | ||
126 | struct GNUNET_CRYPTO_PrivateKey pk; | ||
127 | |||
128 | /** | ||
129 | * Value to return from #main(). | ||
130 | */ | ||
131 | static int global_ret; | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Task run on shutdown. | ||
136 | * | ||
137 | * @param cls NULL | ||
138 | */ | ||
139 | static void | ||
140 | shutdown_task (void *cls) | ||
141 | { | ||
142 | if (NULL != set_op) | ||
143 | { | ||
144 | GNUNET_IDENTITY_cancel (set_op); | ||
145 | set_op = NULL; | ||
146 | } | ||
147 | if (NULL != create_op) | ||
148 | { | ||
149 | GNUNET_IDENTITY_cancel (create_op); | ||
150 | create_op = NULL; | ||
151 | } | ||
152 | if (NULL != delete_op) | ||
153 | { | ||
154 | GNUNET_IDENTITY_cancel (delete_op); | ||
155 | delete_op = NULL; | ||
156 | } | ||
157 | if (NULL != set_ego) | ||
158 | { | ||
159 | GNUNET_free (set_ego); | ||
160 | set_ego = NULL; | ||
161 | } | ||
162 | GNUNET_IDENTITY_disconnect (sh); | ||
163 | sh = NULL; | ||
164 | } | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Test if we are finished yet. | ||
169 | */ | ||
170 | static void | ||
171 | test_finished (void) | ||
172 | { | ||
173 | if ( (NULL == create_op) && | ||
174 | (NULL == delete_op) && | ||
175 | (NULL == set_op) && | ||
176 | (NULL == write_msg) && | ||
177 | (NULL == read_msg) && | ||
178 | (! list) && | ||
179 | (! monitor)) | ||
180 | { | ||
181 | if (TIMEOUT_STATUS_CODE == global_ret) | ||
182 | global_ret = 0; | ||
183 | GNUNET_SCHEDULER_shutdown (); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | |||
188 | /** | ||
189 | * Deletion operation finished. | ||
190 | * | ||
191 | * @param cls pointer to operation handle | ||
192 | * @param ec the error code | ||
193 | */ | ||
194 | static void | ||
195 | delete_finished (void *cls, | ||
196 | enum GNUNET_ErrorCode ec) | ||
197 | { | ||
198 | struct GNUNET_IDENTITY_Operation **op = cls; | ||
199 | |||
200 | *op = NULL; | ||
201 | if (GNUNET_EC_NONE != ec) | ||
202 | fprintf (stderr, "%s\n", GNUNET_ErrorCode_get_hint (ec)); | ||
203 | test_finished (); | ||
204 | } | ||
205 | |||
206 | |||
207 | /** | ||
208 | * Creation operation finished. | ||
209 | * | ||
210 | * @param cls pointer to operation handle | ||
211 | * @param pk private key of the ego, or NULL on error | ||
212 | * @param ec the error code | ||
213 | */ | ||
214 | static void | ||
215 | create_finished (void *cls, | ||
216 | const struct GNUNET_CRYPTO_PrivateKey *pk, | ||
217 | enum GNUNET_ErrorCode ec) | ||
218 | { | ||
219 | struct GNUNET_IDENTITY_Operation **op = cls; | ||
220 | |||
221 | *op = NULL; | ||
222 | if (NULL == pk) | ||
223 | { | ||
224 | fprintf (stderr, | ||
225 | _ ("Failed to create ego: %s\n"), | ||
226 | GNUNET_ErrorCode_get_hint (ec)); | ||
227 | global_ret = 1; | ||
228 | } | ||
229 | else if (verbose) | ||
230 | { | ||
231 | struct GNUNET_CRYPTO_PublicKey pub; | ||
232 | char *pubs; | ||
233 | |||
234 | GNUNET_CRYPTO_key_get_public (pk, &pub); | ||
235 | pubs = GNUNET_CRYPTO_public_key_to_string (&pub); | ||
236 | if (private_keys) | ||
237 | { | ||
238 | char *privs; | ||
239 | |||
240 | privs = GNUNET_CRYPTO_private_key_to_string (pk); | ||
241 | fprintf (stdout, "%s - %s\n", pubs, privs); | ||
242 | GNUNET_free (privs); | ||
243 | } | ||
244 | else | ||
245 | { | ||
246 | fprintf (stdout, "%s\n", pubs); | ||
247 | } | ||
248 | GNUNET_free (pubs); | ||
249 | } | ||
250 | test_finished (); | ||
251 | } | ||
252 | |||
253 | |||
254 | /** | ||
255 | * Encrypt a message given with -W, encrypted using public key of | ||
256 | * an identity given with -k. | ||
257 | */ | ||
258 | static void | ||
259 | write_encrypted_message (void) | ||
260 | { | ||
261 | struct GNUNET_CRYPTO_PublicKey recipient; | ||
262 | size_t ct_len = strlen (write_msg) + 1 | ||
263 | + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES; | ||
264 | unsigned char ct[ct_len]; | ||
265 | if (GNUNET_CRYPTO_public_key_from_string (pubkey_msg, &recipient) != | ||
266 | GNUNET_SYSERR) | ||
267 | { | ||
268 | size_t msg_len = strlen (write_msg) + 1; | ||
269 | if (GNUNET_OK == GNUNET_CRYPTO_encrypt (write_msg, | ||
270 | msg_len, | ||
271 | &recipient, | ||
272 | ct, ct_len)) | ||
273 | { | ||
274 | char *serialized_msg; | ||
275 | serialized_msg = GNUNET_STRINGS_data_to_string_alloc (ct, ct_len); | ||
276 | fprintf (stdout, | ||
277 | "%s\n", | ||
278 | serialized_msg); | ||
279 | GNUNET_free (serialized_msg); | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | fprintf (stderr, "Error during encryption.\n"); | ||
284 | global_ret = 1; | ||
285 | } | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | fprintf (stderr, "Invalid recipient public key.\n"); | ||
290 | global_ret = 1; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | |||
295 | /** | ||
296 | * Decrypt a message given with -R, encrypted using public key of @c ego | ||
297 | * and ephemeral key given with -k. | ||
298 | * | ||
299 | * @param ego ego whose private key is used for decryption | ||
300 | */ | ||
301 | static void | ||
302 | read_encrypted_message (struct GNUNET_IDENTITY_Ego *ego) | ||
303 | { | ||
304 | char *deserialized_msg; | ||
305 | size_t msg_len; | ||
306 | if (GNUNET_OK == GNUNET_STRINGS_string_to_data_alloc (read_msg, strlen ( | ||
307 | read_msg), | ||
308 | (void **) & | ||
309 | deserialized_msg, | ||
310 | &msg_len)) | ||
311 | { | ||
312 | if (GNUNET_OK == GNUNET_CRYPTO_decrypt (deserialized_msg, | ||
313 | msg_len, | ||
314 | GNUNET_IDENTITY_ego_get_private_key ( | ||
315 | ego), | ||
316 | deserialized_msg, msg_len)) | ||
317 | { | ||
318 | deserialized_msg[msg_len - 1] = '\0'; | ||
319 | fprintf (stdout, | ||
320 | "%s\n", | ||
321 | deserialized_msg); | ||
322 | } | ||
323 | else | ||
324 | { | ||
325 | fprintf (stderr, "Failed to decrypt message.\n"); | ||
326 | global_ret = 1; | ||
327 | } | ||
328 | GNUNET_free (deserialized_msg); | ||
329 | } | ||
330 | else | ||
331 | { | ||
332 | fprintf (stderr, "Invalid message format.\n"); | ||
333 | global_ret = 1; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | |||
338 | /** | ||
339 | * If listing is enabled, prints information about the egos. | ||
340 | * | ||
341 | * This function is initially called for all egos and then again | ||
342 | * whenever a ego's identifier changes or if it is deleted. At the | ||
343 | * end of the initial pass over all egos, the function is once called | ||
344 | * with 'NULL' for 'ego'. That does NOT mean that the callback won't | ||
345 | * be invoked in the future or that there was an error. | ||
346 | * | ||
347 | * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this | ||
348 | * function is only called ONCE, and 'NULL' being passed in 'ego' does | ||
349 | * indicate an error (for example because name is taken or no default value is | ||
350 | * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the | ||
351 | * value WILL be passed to a subsequent call to the identity callback of | ||
352 | * 'GNUNET_IDENTITY_connect' (if that one was not NULL). | ||
353 | * | ||
354 | * When an identity is renamed, this function is called with the | ||
355 | * (known) ego but the NEW identifier. | ||
356 | * | ||
357 | * When an identity is deleted, this function is called with the | ||
358 | * (known) ego and "NULL" for the 'identifier'. In this case, | ||
359 | * the 'ego' is henceforth invalid (and the 'ctx' should also be | ||
360 | * cleaned up). | ||
361 | * | ||
362 | * @param cls closure | ||
363 | * @param ego ego handle | ||
364 | * @param ctx context for application to store data for this ego | ||
365 | * (during the lifetime of this process, initially NULL) | ||
366 | * @param identifier identifier assigned by the user for this ego, | ||
367 | * NULL if the user just deleted the ego and it | ||
368 | * must thus no longer be used | ||
369 | */ | ||
370 | static void | ||
371 | print_ego (void *cls, | ||
372 | struct GNUNET_IDENTITY_Ego *ego, | ||
373 | void **ctx, | ||
374 | const char *identifier) | ||
375 | { | ||
376 | struct GNUNET_CRYPTO_PublicKey pk; | ||
377 | char *s; | ||
378 | char *privs; | ||
379 | |||
380 | if ( (NULL == ego) && | ||
381 | (NULL != set_ego) && | ||
382 | (NULL != read_msg) ) | ||
383 | { | ||
384 | fprintf (stderr, | ||
385 | "Ego `%s' is not known, cannot decrypt message.\n", | ||
386 | set_ego); | ||
387 | GNUNET_free (read_msg); | ||
388 | read_msg = NULL; | ||
389 | GNUNET_free (set_ego); | ||
390 | set_ego = NULL; | ||
391 | } | ||
392 | if ((NULL == ego) && (! monitor)) | ||
393 | { | ||
394 | list = 0; | ||
395 | test_finished (); | ||
396 | return; | ||
397 | } | ||
398 | if (! (list | monitor) && (NULL == read_msg)) | ||
399 | return; | ||
400 | if ( (NULL == ego) || | ||
401 | (NULL == identifier) ) | ||
402 | return; | ||
403 | if ( (NULL != set_ego) && | ||
404 | (0 != strcmp (identifier, | ||
405 | set_ego)) ) | ||
406 | return; | ||
407 | GNUNET_IDENTITY_ego_get_public_key (ego, &pk); | ||
408 | s = GNUNET_CRYPTO_public_key_to_string (&pk); | ||
409 | privs = GNUNET_CRYPTO_private_key_to_string ( | ||
410 | GNUNET_IDENTITY_ego_get_private_key (ego)); | ||
411 | if ((NULL != read_msg) && (NULL != set_ego)) | ||
412 | { | ||
413 | // due to the check above, set_ego and the identifier are equal | ||
414 | read_encrypted_message (ego); | ||
415 | GNUNET_free (read_msg); | ||
416 | read_msg = NULL; | ||
417 | } | ||
418 | else if ((monitor) || (NULL != identifier)) | ||
419 | { | ||
420 | if (quiet) | ||
421 | { | ||
422 | if (private_keys) | ||
423 | fprintf (stdout, "%s - %s\n", s, privs); | ||
424 | else | ||
425 | fprintf (stdout, "%s\n", s); | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | if (private_keys) | ||
430 | fprintf (stdout, "%s - %s - %s - %s\n", | ||
431 | identifier, s, privs, | ||
432 | (ntohl (pk.type) == GNUNET_PUBLIC_KEY_TYPE_ECDSA) ? | ||
433 | "ECDSA" : "EdDSA"); | ||
434 | else | ||
435 | fprintf (stdout, "%s - %s - %s\n", | ||
436 | identifier, s, | ||
437 | (ntohl (pk.type) == GNUNET_PUBLIC_KEY_TYPE_ECDSA) ? | ||
438 | "ECDSA" : "EdDSA"); | ||
439 | |||
440 | } | ||
441 | } | ||
442 | GNUNET_free (privs); | ||
443 | GNUNET_free (s); | ||
444 | } | ||
445 | |||
446 | |||
447 | /** | ||
448 | * Main function that will be run by the scheduler. | ||
449 | * | ||
450 | * @param cls closure | ||
451 | * @param args remaining command-line arguments | ||
452 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
453 | * @param cfg configuration | ||
454 | */ | ||
455 | static void | ||
456 | run (void *cls, | ||
457 | char *const *args, | ||
458 | const char *cfgfile, | ||
459 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
460 | { | ||
461 | if ((NULL != read_msg) && (NULL == set_ego)) | ||
462 | { | ||
463 | fprintf (stderr, | ||
464 | "Option -R requires options -e to be specified as well.\n"); | ||
465 | return; | ||
466 | } | ||
467 | |||
468 | if ((NULL != write_msg) && (NULL == pubkey_msg)) | ||
469 | { | ||
470 | fprintf (stderr, "Option -W requires option -k to be specified as well.\n"); | ||
471 | return; | ||
472 | } | ||
473 | sh = GNUNET_IDENTITY_connect (cfg, | ||
474 | (monitor | list) || | ||
475 | (NULL != set_ego) | ||
476 | ? &print_ego | ||
477 | : NULL, | ||
478 | NULL); | ||
479 | if (NULL != write_msg) | ||
480 | { | ||
481 | write_encrypted_message (); | ||
482 | GNUNET_free (write_msg); | ||
483 | write_msg = NULL; | ||
484 | } | ||
485 | // read message is handled in ego callback (print_ego) | ||
486 | if (NULL != delete_ego) | ||
487 | delete_op = | ||
488 | GNUNET_IDENTITY_delete (sh, | ||
489 | delete_ego, | ||
490 | &delete_finished, | ||
491 | &delete_op); | ||
492 | if (NULL != create_ego) | ||
493 | { | ||
494 | if (NULL != privkey_ego) | ||
495 | { | ||
496 | GNUNET_STRINGS_string_to_data (privkey_ego, | ||
497 | strlen (privkey_ego), | ||
498 | &pk, | ||
499 | sizeof(struct | ||
500 | GNUNET_CRYPTO_PrivateKey)); | ||
501 | create_op = | ||
502 | GNUNET_IDENTITY_create (sh, | ||
503 | create_ego, | ||
504 | &pk, | ||
505 | 0, // Ignored | ||
506 | &create_finished, | ||
507 | &create_op); | ||
508 | } | ||
509 | else | ||
510 | create_op = | ||
511 | GNUNET_IDENTITY_create (sh, | ||
512 | create_ego, | ||
513 | NULL, | ||
514 | (type_eddsa) ? | ||
515 | GNUNET_PUBLIC_KEY_TYPE_EDDSA : | ||
516 | GNUNET_PUBLIC_KEY_TYPE_ECDSA, | ||
517 | &create_finished, | ||
518 | &create_op); | ||
519 | } | ||
520 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
521 | NULL); | ||
522 | test_finished (); | ||
523 | } | ||
524 | |||
525 | |||
526 | /** | ||
527 | * The main function. | ||
528 | * | ||
529 | * @param argc number of arguments from the command line | ||
530 | * @param argv command line arguments | ||
531 | * @return 0 ok, 1 on error | ||
532 | */ | ||
533 | int | ||
534 | main (int argc, char *const *argv) | ||
535 | { | ||
536 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
537 | GNUNET_GETOPT_option_string ('C', | ||
538 | "create", | ||
539 | "NAME", | ||
540 | gettext_noop ("create ego NAME"), | ||
541 | &create_ego), | ||
542 | GNUNET_GETOPT_option_string ('D', | ||
543 | "delete", | ||
544 | "NAME", | ||
545 | gettext_noop ("delete ego NAME "), | ||
546 | &delete_ego), | ||
547 | GNUNET_GETOPT_option_string ('P', | ||
548 | "privkey", | ||
549 | "PRIVATE_KEY", | ||
550 | gettext_noop ( | ||
551 | "set the private key for the identity to PRIVATE_KEY (use together with -C)"), | ||
552 | &privkey_ego), | ||
553 | GNUNET_GETOPT_option_string ('R', | ||
554 | "read", | ||
555 | "MESSAGE", | ||
556 | gettext_noop ( | ||
557 | "Read and decrypt message encrypted for the given ego (use together with -e EGO)"), | ||
558 | &read_msg), | ||
559 | GNUNET_GETOPT_option_string ('W', | ||
560 | "write", | ||
561 | "MESSAGE", | ||
562 | gettext_noop ( | ||
563 | "Encrypt and write message for recipient identity PULBIC_KEY, (use together with -k RECIPIENT_PUBLIC_KEY)"), | ||
564 | &write_msg), | ||
565 | GNUNET_GETOPT_option_flag ('X', | ||
566 | "eddsa", | ||
567 | gettext_noop ( | ||
568 | "generate an EdDSA identity. (use together with -C) EXPERIMENTAL"), | ||
569 | &type_eddsa), | ||
570 | GNUNET_GETOPT_option_flag ('d', | ||
571 | "display", | ||
572 | gettext_noop ("display all egos"), | ||
573 | &list), | ||
574 | GNUNET_GETOPT_option_flag ('q', | ||
575 | "quiet", | ||
576 | gettext_noop ("reduce output"), | ||
577 | &quiet), | ||
578 | GNUNET_GETOPT_option_string ( | ||
579 | 'e', | ||
580 | "ego", | ||
581 | "NAME", | ||
582 | gettext_noop ( | ||
583 | "restrict results to NAME (use together with -d) or read and decrypt a message for NAME (use together with -R)"), | ||
584 | &set_ego), | ||
585 | GNUNET_GETOPT_option_string ('k', | ||
586 | "key", | ||
587 | "PUBLIC_KEY", | ||
588 | gettext_noop ( | ||
589 | "The public key of the recipient (with -W)"), | ||
590 | &pubkey_msg), | ||
591 | GNUNET_GETOPT_option_flag ('m', | ||
592 | "monitor", | ||
593 | gettext_noop ("run in monitor mode egos"), | ||
594 | &monitor), | ||
595 | GNUNET_GETOPT_option_flag ('p', | ||
596 | "private-keys", | ||
597 | gettext_noop ("display private keys as well"), | ||
598 | &private_keys), | ||
599 | GNUNET_GETOPT_option_verbose (&verbose), | ||
600 | GNUNET_GETOPT_OPTION_END | ||
601 | }; | ||
602 | int res; | ||
603 | |||
604 | if (GNUNET_OK != | ||
605 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
606 | &argc, &argv)) | ||
607 | return 4; | ||
608 | global_ret = TIMEOUT_STATUS_CODE; /* timeout */ | ||
609 | res = GNUNET_PROGRAM_run (argc, | ||
610 | argv, | ||
611 | "gnunet-identity", | ||
612 | gettext_noop ("Maintain egos"), | ||
613 | options, | ||
614 | &run, | ||
615 | NULL); | ||
616 | GNUNET_free_nz ((void *) argv); | ||
617 | |||
618 | if (GNUNET_OK != res) | ||
619 | return 3; | ||
620 | return global_ret; | ||
621 | } | ||
622 | |||
623 | |||
624 | /* end of gnunet-identity.c */ | ||
diff --git a/src/cli/identity/meson.build b/src/cli/identity/meson.build new file mode 100644 index 000000000..958a4ccd5 --- /dev/null +++ b/src/cli/identity/meson.build | |||
@@ -0,0 +1,9 @@ | |||
1 | executable ('gnunet-identity', | ||
2 | ['gnunet-identity.c'], | ||
3 | dependencies: [libgnunetidentity_dep, | ||
4 | libgnunetutil_dep, | ||
5 | libgnunetstatistics_dep], | ||
6 | include_directories: [incdir, configuration_inc], | ||
7 | install: true, | ||
8 | install_dir: get_option('bindir')) | ||
9 | |||
diff --git a/src/cli/identity/test_identity_messages.sh b/src/cli/identity/test_identity_messages.sh new file mode 100755 index 000000000..edb4d5805 --- /dev/null +++ b/src/cli/identity/test_identity_messages.sh | |||
@@ -0,0 +1,50 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_identity.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_identity.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | |||
20 | TEST_MSG="This is a test message. 123" | ||
21 | gnunet-arm -s -c test_identity.conf | ||
22 | gnunet-identity -C recipientego -c test_identity.conf | ||
23 | gnunet-identity -C recipientegoed -X -c test_identity.conf | ||
24 | RECIPIENT_KEY=`gnunet-identity -d -e recipientego -q -c test_identity.conf` | ||
25 | MSG_ENC=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY -c test_identity.conf` | ||
26 | if [ $? == 0 ] | ||
27 | then | ||
28 | MSG_DEC=`gnunet-identity -R "$MSG_ENC" -e recipientego -c test_identity.conf` | ||
29 | fi | ||
30 | RECIPIENT_KEY_ED=`gnunet-identity -d -e recipientegoed -q -c test_identity.conf` | ||
31 | MSG_ENC_ED=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY_ED -c test_identity.conf` | ||
32 | if [ $? == 0 ] | ||
33 | then | ||
34 | MSG_DEC_ED=`gnunet-identity -R "$MSG_ENC_ED" -e recipientegoed -c test_identity.conf` | ||
35 | fi | ||
36 | gnunet-identity -D recipientego -c test_identity.conf | ||
37 | gnunet-identity -D recipientegoed -c test_identity.conf | ||
38 | gnunet-arm -e -c test_identity.conf | ||
39 | if [ "$TEST_MSG" != "$MSG_DEC" ] | ||
40 | then | ||
41 | diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC") | ||
42 | echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC\"" | ||
43 | exit 1 | ||
44 | fi | ||
45 | if [ "$TEST_MSG" != "$MSG_DEC_ED" ] | ||
46 | then | ||
47 | diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC_ED") | ||
48 | echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC_ED\"" | ||
49 | exit 1 | ||
50 | fi | ||
diff --git a/src/cli/meson.build b/src/cli/meson.build new file mode 100644 index 000000000..12662933d --- /dev/null +++ b/src/cli/meson.build | |||
@@ -0,0 +1,20 @@ | |||
1 | subdir('util') | ||
2 | subdir('arm') | ||
3 | subdir('statistics') | ||
4 | subdir('peerstore') | ||
5 | subdir('datastore') | ||
6 | subdir('nat') | ||
7 | subdir('nat-auto') | ||
8 | subdir('core') | ||
9 | subdir('nse') | ||
10 | subdir('dht') | ||
11 | subdir('identity') | ||
12 | subdir('namecache') | ||
13 | subdir('namestore') | ||
14 | subdir('cadet') | ||
15 | subdir('revocation') | ||
16 | subdir('vpn') | ||
17 | subdir('gns') | ||
18 | subdir('fs') | ||
19 | subdir('reclaim') | ||
20 | subdir('messenger') | ||
diff --git a/src/cli/messenger/.gitignore b/src/cli/messenger/.gitignore new file mode 100644 index 000000000..1c1447be8 --- /dev/null +++ b/src/cli/messenger/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-messenger | |||
diff --git a/src/cli/messenger/Makefile.am b/src/cli/messenger/Makefile.am new file mode 100644 index 000000000..741e2b7b9 --- /dev/null +++ b/src/cli/messenger/Makefile.am | |||
@@ -0,0 +1,25 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | AM_CLFAGS = -g | ||
14 | |||
15 | bin_PROGRAMS = \ | ||
16 | gnunet-messenger | ||
17 | |||
18 | gnunet_messenger_SOURCES = \ | ||
19 | gnunet-messenger.c | ||
20 | gnunet_messenger_LDADD = \ | ||
21 | $(top_builddir)/src/service/messenger/libgnunetmessenger.la \ | ||
22 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
23 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
24 | gnunet_messenger_LDFLAGS = \ | ||
25 | $(GN_LIBINTL) | ||
diff --git a/src/cli/messenger/gnunet-messenger.c b/src/cli/messenger/gnunet-messenger.c new file mode 100644 index 000000000..6bff0b25d --- /dev/null +++ b/src/cli/messenger/gnunet-messenger.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2020--2024 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 | * @author Tobias Frisch | ||
22 | * @file src/messenger/gnunet-messenger.c | ||
23 | * @brief Print information about messenger groups. | ||
24 | */ | ||
25 | |||
26 | #include <stdio.h> | ||
27 | #include <unistd.h> | ||
28 | |||
29 | #include "gnunet_identity_service.h" | ||
30 | #include "gnunet_messenger_service.h" | ||
31 | #include "gnunet_util_lib.h" | ||
32 | |||
33 | const struct GNUNET_CONFIGURATION_Handle *config; | ||
34 | struct GNUNET_MESSENGER_Handle *messenger; | ||
35 | |||
36 | /** | ||
37 | * Function called whenever a message is received or sent. | ||
38 | * | ||
39 | * @param[in,out] cls Closure | ||
40 | * @param[in] room Room | ||
41 | * @param[in] sender Sender of message | ||
42 | * @param[in] message Message | ||
43 | * @param[in] hash Hash of message | ||
44 | * @param[in] flags Flags of message | ||
45 | */ | ||
46 | void | ||
47 | on_message (void *cls, | ||
48 | struct GNUNET_MESSENGER_Room *room, | ||
49 | const struct GNUNET_MESSENGER_Contact *sender, | ||
50 | const struct GNUNET_MESSENGER_Contact *recipient, | ||
51 | const struct GNUNET_MESSENGER_Message *message, | ||
52 | const struct GNUNET_HashCode *hash, | ||
53 | enum GNUNET_MESSENGER_MessageFlags flags) | ||
54 | { | ||
55 | const char *sender_name = GNUNET_MESSENGER_contact_get_name (sender); | ||
56 | const char *recipient_name = GNUNET_MESSENGER_contact_get_name (recipient); | ||
57 | |||
58 | if (! sender_name) | ||
59 | sender_name = "anonymous"; | ||
60 | |||
61 | if (! recipient_name) | ||
62 | recipient_name = "anonymous"; | ||
63 | |||
64 | printf ("[%s ->", GNUNET_h2s (&(message->header.previous))); | ||
65 | printf (" %s]", GNUNET_h2s (hash)); | ||
66 | printf ("[%s] ", GNUNET_sh2s (&(message->header.sender_id))); | ||
67 | |||
68 | if (flags & GNUNET_MESSENGER_FLAG_PRIVATE) | ||
69 | printf ("*( '%s' ) ", recipient_name); | ||
70 | |||
71 | switch (message->header.kind) | ||
72 | { | ||
73 | case GNUNET_MESSENGER_KIND_JOIN: | ||
74 | { | ||
75 | printf ("* '%s' joined the room!\n", sender_name); | ||
76 | break; | ||
77 | } | ||
78 | case GNUNET_MESSENGER_KIND_NAME: | ||
79 | { | ||
80 | printf ("* '%s' gets renamed to '%s'\n", sender_name, | ||
81 | message->body.name.name); | ||
82 | break; | ||
83 | } | ||
84 | case GNUNET_MESSENGER_KIND_LEAVE: | ||
85 | { | ||
86 | printf ("* '%s' leaves the room!\n", sender_name); | ||
87 | break; | ||
88 | } | ||
89 | case GNUNET_MESSENGER_KIND_PEER: | ||
90 | { | ||
91 | printf ("* '%s' opened the room on: %s\n", sender_name, | ||
92 | GNUNET_i2s_full (&(message->body.peer.peer))); | ||
93 | break; | ||
94 | } | ||
95 | case GNUNET_MESSENGER_KIND_TEXT: | ||
96 | { | ||
97 | if (flags & GNUNET_MESSENGER_FLAG_SENT) | ||
98 | printf (">"); | ||
99 | else | ||
100 | printf ("<"); | ||
101 | |||
102 | printf (" '%s' says: \"%s\"\n", sender_name, message->body.text.text); | ||
103 | break; | ||
104 | } | ||
105 | default: | ||
106 | { | ||
107 | printf ("~ message: %s\n", | ||
108 | GNUNET_MESSENGER_name_of_kind (message->header.kind)); | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if ((GNUNET_MESSENGER_KIND_JOIN == message->header.kind) && | ||
114 | (flags & GNUNET_MESSENGER_FLAG_SENT)) | ||
115 | { | ||
116 | const char *name = GNUNET_MESSENGER_get_name (messenger); | ||
117 | |||
118 | if (! name) | ||
119 | return; | ||
120 | |||
121 | struct GNUNET_MESSENGER_Message response; | ||
122 | response.header.kind = GNUNET_MESSENGER_KIND_NAME; | ||
123 | response.body.name.name = GNUNET_strdup (name); | ||
124 | |||
125 | GNUNET_MESSENGER_send_message (room, &response, NULL); | ||
126 | |||
127 | GNUNET_free (response.body.name.name); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | |||
132 | struct GNUNET_SCHEDULER_Task *read_task; | ||
133 | struct GNUNET_IDENTITY_EgoLookup *ego_lookup; | ||
134 | |||
135 | /** | ||
136 | * Task to shut down this application. | ||
137 | * | ||
138 | * @param[in,out] cls Closure | ||
139 | */ | ||
140 | static void | ||
141 | shutdown_hook (void *cls) | ||
142 | { | ||
143 | struct GNUNET_MESSENGER_Room *room = cls; | ||
144 | |||
145 | if (read_task) | ||
146 | GNUNET_SCHEDULER_cancel (read_task); | ||
147 | |||
148 | if (room) | ||
149 | GNUNET_MESSENGER_close_room (room); | ||
150 | |||
151 | if (messenger) | ||
152 | GNUNET_MESSENGER_disconnect (messenger); | ||
153 | |||
154 | if (ego_lookup) | ||
155 | GNUNET_IDENTITY_ego_lookup_cancel (ego_lookup); | ||
156 | } | ||
157 | |||
158 | |||
159 | static void | ||
160 | listen_stdio (void *cls); | ||
161 | |||
162 | #define MAX_BUFFER_SIZE 60000 | ||
163 | |||
164 | static int | ||
165 | iterate_send_private_message (void *cls, | ||
166 | struct GNUNET_MESSENGER_Room *room, | ||
167 | const struct GNUNET_MESSENGER_Contact *contact) | ||
168 | { | ||
169 | struct GNUNET_MESSENGER_Message *message = cls; | ||
170 | |||
171 | if (GNUNET_MESSENGER_contact_get_key (contact)) | ||
172 | GNUNET_MESSENGER_send_message (room, message, contact); | ||
173 | |||
174 | return GNUNET_YES; | ||
175 | } | ||
176 | |||
177 | |||
178 | int private_mode; | ||
179 | |||
180 | /** | ||
181 | * Task run in stdio mode, after some data is available at stdin. | ||
182 | * | ||
183 | * @param[in,out] cls Closure | ||
184 | */ | ||
185 | static void | ||
186 | read_stdio (void *cls) | ||
187 | { | ||
188 | read_task = NULL; | ||
189 | |||
190 | char buffer[MAX_BUFFER_SIZE]; | ||
191 | ssize_t length; | ||
192 | |||
193 | length = read (0, buffer, MAX_BUFFER_SIZE); | ||
194 | |||
195 | if ((length <= 0) || (length >= MAX_BUFFER_SIZE)) | ||
196 | { | ||
197 | GNUNET_SCHEDULER_shutdown (); | ||
198 | return; | ||
199 | } | ||
200 | |||
201 | if (buffer[length - 1] == '\n') | ||
202 | buffer[length - 1] = '\0'; | ||
203 | else | ||
204 | buffer[length] = '\0'; | ||
205 | |||
206 | struct GNUNET_MESSENGER_Room *room = cls; | ||
207 | |||
208 | struct GNUNET_MESSENGER_Message message; | ||
209 | message.header.kind = GNUNET_MESSENGER_KIND_TEXT; | ||
210 | message.body.text.text = buffer; | ||
211 | |||
212 | if (GNUNET_YES == private_mode) | ||
213 | GNUNET_MESSENGER_iterate_members (room, iterate_send_private_message, | ||
214 | &message); | ||
215 | else | ||
216 | GNUNET_MESSENGER_send_message (room, &message, NULL); | ||
217 | |||
218 | read_task = GNUNET_SCHEDULER_add_now (listen_stdio, cls); | ||
219 | } | ||
220 | |||
221 | |||
222 | /** | ||
223 | * Wait for input on STDIO and send it out over the #ch. | ||
224 | * | ||
225 | * @param[in,out] cls Closure | ||
226 | */ | ||
227 | static void | ||
228 | listen_stdio (void *cls) | ||
229 | { | ||
230 | read_task = NULL; | ||
231 | |||
232 | struct GNUNET_NETWORK_FDSet *rs = GNUNET_NETWORK_fdset_create (); | ||
233 | |||
234 | GNUNET_NETWORK_fdset_set_native (rs, 0); | ||
235 | |||
236 | read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
237 | GNUNET_TIME_UNIT_FOREVER_REL, rs, | ||
238 | NULL, &read_stdio, cls); | ||
239 | |||
240 | GNUNET_NETWORK_fdset_destroy (rs); | ||
241 | } | ||
242 | |||
243 | |||
244 | /** | ||
245 | * Initial task to startup application. | ||
246 | * | ||
247 | * @param[in,out] cls Closure | ||
248 | */ | ||
249 | static void | ||
250 | idle (void *cls) | ||
251 | { | ||
252 | struct GNUNET_MESSENGER_Room *room = cls; | ||
253 | |||
254 | printf ("* You joined the room.\n"); | ||
255 | |||
256 | read_task = GNUNET_SCHEDULER_add_now (listen_stdio, room); | ||
257 | } | ||
258 | |||
259 | |||
260 | char *door_id; | ||
261 | char *ego_name; | ||
262 | char *room_key; | ||
263 | |||
264 | struct GNUNET_SCHEDULER_Task *shutdown_task; | ||
265 | |||
266 | /** | ||
267 | * Function called when an identity is retrieved. | ||
268 | * | ||
269 | * @param[in,out] cls Closure | ||
270 | * @param[in,out] handle Handle of messenger service | ||
271 | */ | ||
272 | static void | ||
273 | on_identity (void *cls, | ||
274 | struct GNUNET_MESSENGER_Handle *handle) | ||
275 | { | ||
276 | struct GNUNET_HashCode key; | ||
277 | memset (&key, 0, sizeof(key)); | ||
278 | |||
279 | if (room_key) | ||
280 | GNUNET_CRYPTO_hash (room_key, strlen (room_key), &key); | ||
281 | |||
282 | struct GNUNET_PeerIdentity door_peer; | ||
283 | struct GNUNET_PeerIdentity *door = NULL; | ||
284 | |||
285 | if ((door_id) && | ||
286 | (GNUNET_OK == GNUNET_CRYPTO_eddsa_public_key_from_string (door_id, | ||
287 | strlen ( | ||
288 | door_id), | ||
289 | &(door_peer. | ||
290 | public_key)))) | ||
291 | door = &door_peer; | ||
292 | |||
293 | const char *name = GNUNET_MESSENGER_get_name (handle); | ||
294 | |||
295 | if (! name) | ||
296 | name = "anonymous"; | ||
297 | |||
298 | printf ("* Welcome to the messenger, '%s'!\n", name); | ||
299 | |||
300 | struct GNUNET_MESSENGER_Room *room; | ||
301 | |||
302 | if (door) | ||
303 | { | ||
304 | printf ("* You try to entry a room...\n"); | ||
305 | |||
306 | room = GNUNET_MESSENGER_enter_room (messenger, door, &key); | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | printf ("* You try to open a room...\n"); | ||
311 | |||
312 | room = GNUNET_MESSENGER_open_room (messenger, &key); | ||
313 | } | ||
314 | |||
315 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
316 | |||
317 | shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, room); | ||
318 | |||
319 | if (! room) | ||
320 | GNUNET_SCHEDULER_shutdown (); | ||
321 | else | ||
322 | { | ||
323 | GNUNET_SCHEDULER_add_delayed_with_priority ( | ||
324 | GNUNET_TIME_relative_get_zero_ (), | ||
325 | GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
326 | idle, room); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | |||
331 | static void | ||
332 | on_ego_lookup (void *cls, | ||
333 | struct GNUNET_IDENTITY_Ego *ego) | ||
334 | { | ||
335 | ego_lookup = NULL; | ||
336 | |||
337 | const struct GNUNET_CRYPTO_PrivateKey *key; | ||
338 | key = ego ? GNUNET_IDENTITY_ego_get_private_key (ego) : NULL; | ||
339 | |||
340 | messenger = GNUNET_MESSENGER_connect (config, ego_name, key, &on_message, | ||
341 | NULL); | ||
342 | |||
343 | on_identity (NULL, messenger); | ||
344 | } | ||
345 | |||
346 | |||
347 | /** | ||
348 | * Main function that will be run by the scheduler. | ||
349 | * | ||
350 | * @param[in/out] cls closure | ||
351 | * @param[in] args remaining command-line arguments | ||
352 | * @param[in] cfgfile name of the configuration file used (for saving, can be NULL!) | ||
353 | * @param[in] cfg configuration | ||
354 | */ | ||
355 | static void | ||
356 | run (void *cls, | ||
357 | char *const *args, | ||
358 | const char *cfgfile, | ||
359 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
360 | { | ||
361 | config = cfg; | ||
362 | |||
363 | if (ego_name) | ||
364 | { | ||
365 | ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &on_ego_lookup, | ||
366 | NULL); | ||
367 | messenger = NULL; | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | ego_lookup = NULL; | ||
372 | messenger = GNUNET_MESSENGER_connect (cfg, NULL, NULL, &on_message, NULL); | ||
373 | } | ||
374 | |||
375 | shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, NULL); | ||
376 | |||
377 | if (messenger) | ||
378 | on_identity (NULL, messenger); | ||
379 | } | ||
380 | |||
381 | |||
382 | /** | ||
383 | * The main function to obtain messenger information. | ||
384 | * | ||
385 | * @param[in] argc number of arguments from the command line | ||
386 | * @param[in] argv command line arguments | ||
387 | * @return #EXIT_SUCCESS ok, #EXIT_FAILURE on error | ||
388 | */ | ||
389 | int | ||
390 | main (int argc, | ||
391 | char **argv) | ||
392 | { | ||
393 | const char *description = | ||
394 | "Open and connect to rooms using the MESSENGER to chat."; | ||
395 | |||
396 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
397 | GNUNET_GETOPT_option_string ('d', "door", "PEERIDENTITY", | ||
398 | "peer identity to entry into the room", | ||
399 | &door_id), | ||
400 | GNUNET_GETOPT_option_string ('e', "ego", "IDENTITY", | ||
401 | "identity to use for messaging", | ||
402 | &ego_name), | ||
403 | GNUNET_GETOPT_option_string ('r', "room", "ROOMKEY", | ||
404 | "key of the room to connect to", | ||
405 | &room_key), | ||
406 | GNUNET_GETOPT_option_flag ('p', "private", "flag to enable private mode", | ||
407 | &private_mode), | ||
408 | GNUNET_GETOPT_OPTION_END | ||
409 | }; | ||
410 | |||
411 | return (GNUNET_OK == GNUNET_PROGRAM_run (argc, argv, "gnunet-messenger\0", | ||
412 | gettext_noop (description), options, | ||
413 | &run, | ||
414 | NULL) ? EXIT_SUCCESS : EXIT_FAILURE); | ||
415 | } | ||
diff --git a/src/cli/messenger/meson.build b/src/cli/messenger/meson.build new file mode 100644 index 000000000..3a3870c9d --- /dev/null +++ b/src/cli/messenger/meson.build | |||
@@ -0,0 +1,9 @@ | |||
1 | executable ('gnunet-messenger', | ||
2 | 'gnunet-messenger.c', | ||
3 | dependencies: [libgnunetmessenger_dep, | ||
4 | libgnunetidentity_dep, | ||
5 | libgnunetutil_dep], | ||
6 | include_directories: [incdir, configuration_inc], | ||
7 | install: true, | ||
8 | install_dir: get_option('bindir')) | ||
9 | |||
diff --git a/src/cli/namecache/.gitignore b/src/cli/namecache/.gitignore new file mode 100644 index 000000000..54aa17e9f --- /dev/null +++ b/src/cli/namecache/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-namecache | |||
diff --git a/src/cli/namecache/Makefile.am b/src/cli/namecache/Makefile.am new file mode 100644 index 000000000..48ee216be --- /dev/null +++ b/src/cli/namecache/Makefile.am | |||
@@ -0,0 +1,20 @@ | |||
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 | bin_PROGRAMS = \ | ||
11 | gnunet-namecache | ||
12 | |||
13 | gnunet_namecache_SOURCES = \ | ||
14 | gnunet-namecache.c | ||
15 | gnunet_namecache_LDADD = \ | ||
16 | $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ | ||
17 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
18 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
19 | $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ | ||
20 | $(GN_LIBINTL) | ||
diff --git a/src/cli/namecache/gnunet-namecache.c b/src/cli/namecache/gnunet-namecache.c new file mode 100644 index 000000000..0236609aa --- /dev/null +++ b/src/cli/namecache/gnunet-namecache.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012, 2013 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-namecache.c | ||
22 | * @brief command line tool to inspect the name cache | ||
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_namecache_service.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Handle to the namecache. | ||
37 | */ | ||
38 | static struct GNUNET_NAMECACHE_Handle *ns; | ||
39 | |||
40 | /** | ||
41 | * Queue entry for the 'query' operation. | ||
42 | */ | ||
43 | static struct GNUNET_NAMECACHE_QueueEntry *qe; | ||
44 | |||
45 | /** | ||
46 | * Name (label) of the records to list. | ||
47 | */ | ||
48 | static char *name; | ||
49 | |||
50 | /** | ||
51 | * Public key of the zone to look in. | ||
52 | */ | ||
53 | static struct GNUNET_CRYPTO_PublicKey pubkey; | ||
54 | |||
55 | /** | ||
56 | * Public key of the zone to look in, in ASCII. | ||
57 | */ | ||
58 | static char *pkey; | ||
59 | |||
60 | /** | ||
61 | * Global return value | ||
62 | */ | ||
63 | static int ret; | ||
64 | |||
65 | |||
66 | /** | ||
67 | * Task run on shutdown. Cleans up everything. | ||
68 | * | ||
69 | * @param cls unused | ||
70 | */ | ||
71 | static void | ||
72 | do_shutdown (void *cls) | ||
73 | { | ||
74 | if (NULL != qe) | ||
75 | { | ||
76 | GNUNET_NAMECACHE_cancel (qe); | ||
77 | qe = NULL; | ||
78 | } | ||
79 | if (NULL != ns) | ||
80 | { | ||
81 | GNUNET_NAMECACHE_disconnect (ns); | ||
82 | ns = NULL; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Process a record that was stored in the namecache in a block. | ||
89 | * | ||
90 | * @param cls closure, NULL | ||
91 | * @param rd_len number of entries in @a rd array | ||
92 | * @param rd array of records with data to store | ||
93 | */ | ||
94 | static void | ||
95 | display_records_from_block (void *cls, | ||
96 | unsigned int rd_len, | ||
97 | const struct GNUNET_GNSRECORD_Data *rd) | ||
98 | { | ||
99 | const char *typestring; | ||
100 | char *s; | ||
101 | unsigned int i; | ||
102 | |||
103 | if (0 == rd_len) | ||
104 | { | ||
105 | fprintf (stdout, _ ("No records found for `%s'"), name); | ||
106 | return; | ||
107 | } | ||
108 | fprintf (stdout, "%s:\n", name); | ||
109 | for (i = 0; i < rd_len; i++) | ||
110 | { | ||
111 | typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type); | ||
112 | s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type, | ||
113 | rd[i].data, | ||
114 | rd[i].data_size); | ||
115 | if (NULL == s) | ||
116 | { | ||
117 | fprintf (stdout, | ||
118 | _ ("\tCorrupt or unsupported record of type %u\n"), | ||
119 | (unsigned int) rd[i].record_type); | ||
120 | continue; | ||
121 | } | ||
122 | fprintf (stdout, "\t%s: %s\n", typestring, s); | ||
123 | GNUNET_free (s); | ||
124 | } | ||
125 | fprintf (stdout, "%s", "\n"); | ||
126 | } | ||
127 | |||
128 | |||
129 | /** | ||
130 | * Display block obtained from listing (by name). | ||
131 | * | ||
132 | * @param cls NULL | ||
133 | * @param block NULL if not found | ||
134 | */ | ||
135 | static void | ||
136 | handle_block (void *cls, const struct GNUNET_GNSRECORD_Block *block) | ||
137 | { | ||
138 | qe = NULL; | ||
139 | if (NULL == block) | ||
140 | { | ||
141 | fprintf (stderr, "No matching block found\n"); | ||
142 | } | ||
143 | else if (GNUNET_OK != | ||
144 | GNUNET_GNSRECORD_block_decrypt (block, | ||
145 | &pubkey, | ||
146 | name, | ||
147 | &display_records_from_block, | ||
148 | NULL)) | ||
149 | { | ||
150 | fprintf (stderr, "Failed to decrypt block!\n"); | ||
151 | } | ||
152 | GNUNET_SCHEDULER_shutdown (); | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Main function that will be run. | ||
158 | * | ||
159 | * @param cls closure | ||
160 | * @param args remaining command-line arguments | ||
161 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
162 | * @param cfg configuration | ||
163 | */ | ||
164 | static void | ||
165 | run (void *cls, | ||
166 | char *const *args, | ||
167 | const char *cfgfile, | ||
168 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
169 | { | ||
170 | struct GNUNET_HashCode dhash; | ||
171 | |||
172 | if (NULL == pkey) | ||
173 | { | ||
174 | fprintf (stderr, _ ("You must specify which zone should be accessed\n")); | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | if (GNUNET_OK != | ||
179 | GNUNET_CRYPTO_public_key_from_string (pkey, &pubkey)) | ||
180 | { | ||
181 | fprintf (stderr, _ ("Invalid public key for zone `%s'\n"), pkey); | ||
182 | GNUNET_SCHEDULER_shutdown (); | ||
183 | return; | ||
184 | } | ||
185 | if (NULL == name) | ||
186 | { | ||
187 | fprintf (stderr, _ ("You must specify a name\n")); | ||
188 | return; | ||
189 | } | ||
190 | |||
191 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
192 | ns = GNUNET_NAMECACHE_connect (cfg); | ||
193 | GNUNET_GNSRECORD_query_from_public_key (&pubkey, name, &dhash); | ||
194 | qe = GNUNET_NAMECACHE_lookup_block (ns, &dhash, &handle_block, NULL); | ||
195 | } | ||
196 | |||
197 | |||
198 | /** | ||
199 | * The main function for gnunet-namecache. | ||
200 | * | ||
201 | * @param argc number of arguments from the command line | ||
202 | * @param argv command line arguments | ||
203 | * @return 0 ok, 1 on error | ||
204 | */ | ||
205 | int | ||
206 | main (int argc, char *const *argv) | ||
207 | { | ||
208 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
209 | { GNUNET_GETOPT_option_string ('n', | ||
210 | "name", | ||
211 | "NAME", | ||
212 | gettext_noop ( | ||
213 | "name of the record to add/delete/display"), | ||
214 | &name), | ||
215 | |||
216 | GNUNET_GETOPT_option_string ( | ||
217 | 'z', | ||
218 | "zone", | ||
219 | "PKEY", | ||
220 | gettext_noop ("specifies the public key of the zone to look in"), | ||
221 | &pkey), | ||
222 | |||
223 | GNUNET_GETOPT_OPTION_END }; | ||
224 | |||
225 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
226 | return 2; | ||
227 | |||
228 | GNUNET_log_setup ("gnunet-namecache", "WARNING", NULL); | ||
229 | if (GNUNET_OK != GNUNET_PROGRAM_run (argc, | ||
230 | argv, | ||
231 | "gnunet-namecache", | ||
232 | _ ("GNUnet zone manipulation tool"), | ||
233 | options, | ||
234 | &run, | ||
235 | NULL)) | ||
236 | { | ||
237 | GNUNET_free_nz ((void *) argv); | ||
238 | return 1; | ||
239 | } | ||
240 | GNUNET_free_nz ((void *) argv); | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | |||
245 | /* end of gnunet-namecache.c */ | ||
diff --git a/src/cli/namecache/meson.build b/src/cli/namecache/meson.build new file mode 100644 index 000000000..99ed68bca --- /dev/null +++ b/src/cli/namecache/meson.build | |||
@@ -0,0 +1,10 @@ | |||
1 | executable ('gnunet-namecache', | ||
2 | gnunetnamecache_src, | ||
3 | dependencies: [libgnunetnamecache_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 | |||
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 | |||
diff --git a/src/cli/nat-auto/.gitignore b/src/cli/nat-auto/.gitignore new file mode 100644 index 000000000..c750bf612 --- /dev/null +++ b/src/cli/nat-auto/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-nat-auto | ||
2 | gnunet-nat-server | ||
diff --git a/src/cli/nat-auto/Makefile.am b/src/cli/nat-auto/Makefile.am new file mode 100644 index 000000000..4b2d74280 --- /dev/null +++ b/src/cli/nat-auto/Makefile.am | |||
@@ -0,0 +1,26 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | libexecdir= $(pkglibdir)/libexec/ | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | bin_PROGRAMS = \ | ||
9 | gnunet-nat-auto \ | ||
10 | gnunet-nat-server | ||
11 | |||
12 | gnunet_nat_server_SOURCES = \ | ||
13 | gnunet-nat-server.c | ||
14 | gnunet_nat_server_LDADD = \ | ||
15 | $(top_builddir)/src/service/nat/libgnunetnatnew.la \ | ||
16 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
17 | gnunet_nat_server_LDFLAGS = \ | ||
18 | $(GN_LIBINTL) | ||
19 | |||
20 | gnunet_nat_auto_SOURCES = \ | ||
21 | gnunet-nat-auto.c | ||
22 | gnunet_nat_auto_LDADD = \ | ||
23 | $(top_builddir)/src/service/nat-auto/libgnunetnatauto.la \ | ||
24 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
25 | gnunet_nat_auto_LDFLAGS = \ | ||
26 | $(GN_LIBINTL) | ||
diff --git a/src/cli/nat-auto/gnunet-nat-auto.c b/src/cli/nat-auto/gnunet-nat-auto.c new file mode 100644 index 000000000..055a949bd --- /dev/null +++ b/src/cli/nat-auto/gnunet-nat-auto.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2015, 2016, 2017 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/nat/gnunet-nat-auto.c | ||
23 | * @brief Command-line tool for testing and autoconfiguration of NAT traversal | ||
24 | * @author Christian Grothoff | ||
25 | * @author Bruno Cabral | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_nat_service.h" | ||
30 | #include "gnunet_nat_auto_service.h" | ||
31 | |||
32 | /** | ||
33 | * Value to return from #main(). | ||
34 | */ | ||
35 | static int global_ret; | ||
36 | |||
37 | /** | ||
38 | * Handle to ongoing autoconfiguration. | ||
39 | */ | ||
40 | static struct GNUNET_NAT_AUTO_AutoHandle *ah; | ||
41 | |||
42 | /** | ||
43 | * If we do auto-configuration, should we write the result | ||
44 | * to a file? | ||
45 | */ | ||
46 | static int write_cfg; | ||
47 | |||
48 | /** | ||
49 | * Configuration filename. | ||
50 | */ | ||
51 | static const char *cfg_file; | ||
52 | |||
53 | /** | ||
54 | * Original configuration. | ||
55 | */ | ||
56 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
57 | |||
58 | /** | ||
59 | * Adapter we are supposed to test. | ||
60 | */ | ||
61 | static char *section_name; | ||
62 | |||
63 | /** | ||
64 | * Should we run autoconfiguration? | ||
65 | */ | ||
66 | static int do_auto; | ||
67 | |||
68 | /** | ||
69 | * Handle to a NAT test operation. | ||
70 | */ | ||
71 | static struct GNUNET_NAT_AUTO_Test *nt; | ||
72 | |||
73 | /** | ||
74 | * Flag set to 1 if we use IPPROTO_UDP. | ||
75 | */ | ||
76 | static int use_udp; | ||
77 | |||
78 | /** | ||
79 | * Flag set to 1 if we use IPPROTO_TCP. | ||
80 | */ | ||
81 | static int use_tcp; | ||
82 | |||
83 | /** | ||
84 | * Protocol to use. | ||
85 | */ | ||
86 | static uint8_t proto; | ||
87 | |||
88 | /** | ||
89 | * Test if all activities have finished, and if so, | ||
90 | * terminate. | ||
91 | */ | ||
92 | static void | ||
93 | test_finished () | ||
94 | { | ||
95 | if (NULL != ah) | ||
96 | return; | ||
97 | if (NULL != nt) | ||
98 | return; | ||
99 | GNUNET_SCHEDULER_shutdown (); | ||
100 | } | ||
101 | |||
102 | |||
103 | /** | ||
104 | * Function to iterate over suggested changes options | ||
105 | * | ||
106 | * @param cls closure | ||
107 | * @param section name of the section | ||
108 | * @param option name of the option | ||
109 | * @param value value of the option | ||
110 | */ | ||
111 | static void | ||
112 | auto_conf_iter (void *cls, | ||
113 | const char *section, | ||
114 | const char *option, | ||
115 | const char *value) | ||
116 | { | ||
117 | struct GNUNET_CONFIGURATION_Handle *new_cfg = cls; | ||
118 | |||
119 | printf ("%s: %s\n", option, value); | ||
120 | if (NULL != new_cfg) | ||
121 | GNUNET_CONFIGURATION_set_value_string (new_cfg, section, option, value); | ||
122 | } | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Function called with the result from the autoconfiguration. | ||
127 | * | ||
128 | * @param cls closure | ||
129 | * @param diff minimal suggested changes to the original configuration | ||
130 | * to make it work (as best as we can) | ||
131 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code | ||
132 | * @param type what the situation of the NAT | ||
133 | */ | ||
134 | static void | ||
135 | auto_config_cb (void *cls, | ||
136 | const struct GNUNET_CONFIGURATION_Handle *diff, | ||
137 | enum GNUNET_NAT_StatusCode result, | ||
138 | enum GNUNET_NAT_Type type) | ||
139 | { | ||
140 | const char *nat_type; | ||
141 | char unknown_type[64]; | ||
142 | struct GNUNET_CONFIGURATION_Handle *new_cfg; | ||
143 | |||
144 | ah = NULL; | ||
145 | switch (type) | ||
146 | { | ||
147 | case GNUNET_NAT_TYPE_NO_NAT: | ||
148 | nat_type = "NO NAT"; | ||
149 | break; | ||
150 | |||
151 | case GNUNET_NAT_TYPE_UNREACHABLE_NAT: | ||
152 | nat_type = "NAT but we can traverse"; | ||
153 | break; | ||
154 | |||
155 | case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT: | ||
156 | nat_type = "NAT but STUN is able to identify the correct information"; | ||
157 | break; | ||
158 | |||
159 | case GNUNET_NAT_TYPE_UPNP_NAT: | ||
160 | nat_type = "NAT but UPNP opened the ports"; | ||
161 | break; | ||
162 | |||
163 | default: | ||
164 | sprintf (unknown_type, "NAT unknown, type %u", type); | ||
165 | nat_type = unknown_type; | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
170 | "NAT status: %s/%s\n", | ||
171 | GNUNET_NAT_AUTO_status2string (result), | ||
172 | nat_type); | ||
173 | |||
174 | if (NULL == diff) | ||
175 | return; | ||
176 | |||
177 | /* Shortcut: if there are no changes suggested, bail out early. */ | ||
178 | if (GNUNET_NO == GNUNET_CONFIGURATION_is_dirty (diff)) | ||
179 | { | ||
180 | test_finished (); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | /* Apply diff to original configuration and show changes | ||
185 | to the user */ | ||
186 | new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL; | ||
187 | |||
188 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
189 | _ ("Suggested configuration changes:\n")); | ||
190 | GNUNET_CONFIGURATION_iterate_section_values (diff, | ||
191 | "nat", | ||
192 | &auto_conf_iter, | ||
193 | new_cfg); | ||
194 | |||
195 | /* If desired, write configuration to file; we write only the | ||
196 | changes to the defaults to keep things compact. */ | ||
197 | if (write_cfg) | ||
198 | { | ||
199 | struct GNUNET_CONFIGURATION_Handle *def_cfg; | ||
200 | |||
201 | GNUNET_CONFIGURATION_set_value_string (new_cfg, "ARM", "CONFIG", NULL); | ||
202 | def_cfg = GNUNET_CONFIGURATION_create (); | ||
203 | GNUNET_break (GNUNET_OK == GNUNET_CONFIGURATION_load (def_cfg, NULL)); | ||
204 | if (GNUNET_OK != | ||
205 | GNUNET_CONFIGURATION_write_diffs (def_cfg, new_cfg, cfg_file)) | ||
206 | { | ||
207 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
208 | _ ("Failed to write configuration to `%s'\n"), | ||
209 | cfg_file); | ||
210 | global_ret = 1; | ||
211 | } | ||
212 | else | ||
213 | { | ||
214 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
215 | _ ("Wrote updated configuration to `%s'\n"), | ||
216 | cfg_file); | ||
217 | } | ||
218 | GNUNET_CONFIGURATION_destroy (def_cfg); | ||
219 | } | ||
220 | |||
221 | if (NULL != new_cfg) | ||
222 | GNUNET_CONFIGURATION_destroy (new_cfg); | ||
223 | test_finished (); | ||
224 | } | ||
225 | |||
226 | |||
227 | /** | ||
228 | * Function called to report success or failure for | ||
229 | * NAT configuration test. | ||
230 | * | ||
231 | * @param cls closure | ||
232 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code | ||
233 | */ | ||
234 | static void | ||
235 | test_report_cb (void *cls, enum GNUNET_NAT_StatusCode result) | ||
236 | { | ||
237 | nt = NULL; | ||
238 | printf ("NAT test result: %s\n", GNUNET_NAT_AUTO_status2string (result)); | ||
239 | test_finished (); | ||
240 | } | ||
241 | |||
242 | |||
243 | /** | ||
244 | * Task run on shutdown. | ||
245 | * | ||
246 | * @param cls NULL | ||
247 | */ | ||
248 | static void | ||
249 | do_shutdown (void *cls) | ||
250 | { | ||
251 | if (NULL != ah) | ||
252 | { | ||
253 | GNUNET_NAT_AUTO_autoconfig_cancel (ah); | ||
254 | ah = NULL; | ||
255 | } | ||
256 | if (NULL != nt) | ||
257 | { | ||
258 | GNUNET_NAT_AUTO_test_stop (nt); | ||
259 | nt = NULL; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Main function that will be run. | ||
266 | * | ||
267 | * @param cls closure | ||
268 | * @param args remaining command-line arguments | ||
269 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
270 | * @param c configuration | ||
271 | */ | ||
272 | static void | ||
273 | run (void *cls, | ||
274 | char *const *args, | ||
275 | const char *cfgfile, | ||
276 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
277 | { | ||
278 | cfg_file = cfgfile; | ||
279 | cfg = c; | ||
280 | |||
281 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
282 | |||
283 | if (do_auto) | ||
284 | { | ||
285 | ah = GNUNET_NAT_AUTO_autoconfig_start (c, &auto_config_cb, NULL); | ||
286 | } | ||
287 | |||
288 | if (use_tcp && use_udp) | ||
289 | { | ||
290 | if (do_auto) | ||
291 | return; | ||
292 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); | ||
293 | global_ret = 1; | ||
294 | return; | ||
295 | } | ||
296 | proto = 0; | ||
297 | if (use_tcp) | ||
298 | proto = IPPROTO_TCP; | ||
299 | if (use_udp) | ||
300 | proto = IPPROTO_UDP; | ||
301 | |||
302 | if (NULL != section_name) | ||
303 | { | ||
304 | nt = GNUNET_NAT_AUTO_test_start (c, | ||
305 | proto, | ||
306 | section_name, | ||
307 | &test_report_cb, | ||
308 | NULL); | ||
309 | } | ||
310 | test_finished (); | ||
311 | } | ||
312 | |||
313 | |||
314 | /** | ||
315 | * Main function of gnunet-nat-auto | ||
316 | * | ||
317 | * @param argc number of command-line arguments | ||
318 | * @param argv command line | ||
319 | * @return 0 on success, -1 on error | ||
320 | */ | ||
321 | int | ||
322 | main (int argc, char *const argv[]) | ||
323 | { | ||
324 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
325 | { GNUNET_GETOPT_option_flag ('a', | ||
326 | "auto", | ||
327 | gettext_noop ("run autoconfiguration"), | ||
328 | &do_auto), | ||
329 | |||
330 | GNUNET_GETOPT_option_string ( | ||
331 | 'S', | ||
332 | "section", | ||
333 | "NAME", | ||
334 | gettext_noop ( | ||
335 | "section name providing the configuration for the adapter"), | ||
336 | §ion_name), | ||
337 | |||
338 | GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp), | ||
339 | |||
340 | GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp), | ||
341 | |||
342 | GNUNET_GETOPT_option_flag ( | ||
343 | 'w', | ||
344 | "write", | ||
345 | gettext_noop ("write configuration file (for autoconfiguration)"), | ||
346 | &write_cfg), | ||
347 | GNUNET_GETOPT_OPTION_END }; | ||
348 | |||
349 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
350 | return 2; | ||
351 | if (GNUNET_OK != | ||
352 | GNUNET_PROGRAM_run (argc, | ||
353 | argv, | ||
354 | "gnunet-nat-auto [options]", | ||
355 | _ ("GNUnet NAT traversal autoconfiguration"), | ||
356 | options, | ||
357 | &run, | ||
358 | NULL)) | ||
359 | { | ||
360 | global_ret = 1; | ||
361 | } | ||
362 | GNUNET_free_nz ((void *) argv); | ||
363 | return global_ret; | ||
364 | } | ||
365 | |||
366 | |||
367 | /* end of gnunet-nat-auto.c */ | ||
diff --git a/src/cli/nat-auto/gnunet-nat-server.c b/src/cli/nat-auto/gnunet-nat-server.c new file mode 100644 index 000000000..baa610e8f --- /dev/null +++ b/src/cli/nat-auto/gnunet-nat-server.c | |||
@@ -0,0 +1,403 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011, 2017 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/nat/gnunet-nat-server.c | ||
23 | * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_nat_service.h" | ||
29 | #include "gnunet_protocols.h" | ||
30 | // FIXME can we build this without this header? | ||
31 | #include "../../service/nat-auto/nat-auto.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Information we track per client. | ||
36 | */ | ||
37 | struct ClientData | ||
38 | { | ||
39 | /** | ||
40 | * Timeout task. | ||
41 | */ | ||
42 | struct GNUNET_SCHEDULER_Task *tt; | ||
43 | |||
44 | /** | ||
45 | * Client handle. | ||
46 | */ | ||
47 | struct GNUNET_SERVICE_Client *client; | ||
48 | }; | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Our configuration. | ||
53 | */ | ||
54 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
55 | |||
56 | |||
57 | /** | ||
58 | * Try contacting the peer using autonomous NAT traversal method. | ||
59 | * | ||
60 | * @param dst_ipv4 IPv4 address to send the fake ICMP message | ||
61 | * @param dport destination port to include in ICMP message | ||
62 | * @param is_tcp mark for TCP (#GNUNET_YES) or UDP (#GNUNET_NO) | ||
63 | */ | ||
64 | static void | ||
65 | try_anat (uint32_t dst_ipv4, | ||
66 | uint16_t dport, | ||
67 | int is_tcp) | ||
68 | { | ||
69 | struct GNUNET_NAT_Handle *h; | ||
70 | struct sockaddr_in lsa; | ||
71 | struct sockaddr_in rsa; | ||
72 | const struct sockaddr *sa; | ||
73 | socklen_t sa_len; | ||
74 | |||
75 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
76 | "Asking for connection reversal with %x and code %u\n", | ||
77 | (unsigned int) dst_ipv4, | ||
78 | (unsigned int) dport); | ||
79 | memset (&lsa, 0, sizeof(lsa)); | ||
80 | lsa.sin_family = AF_INET; | ||
81 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
82 | lsa.sin_len = sizeof(sa); | ||
83 | #endif | ||
84 | lsa.sin_addr.s_addr = 0; | ||
85 | lsa.sin_port = htons (dport); | ||
86 | memset (&rsa, 0, sizeof(rsa)); | ||
87 | rsa.sin_family = AF_INET; | ||
88 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
89 | rsa.sin_len = sizeof(sa); | ||
90 | #endif | ||
91 | rsa.sin_addr.s_addr = dst_ipv4; | ||
92 | rsa.sin_port = htons (dport); | ||
93 | sa_len = sizeof(lsa); | ||
94 | sa = (const struct sockaddr *) &lsa; | ||
95 | h = GNUNET_NAT_register (cfg, | ||
96 | "none", | ||
97 | is_tcp ? IPPROTO_TCP : IPPROTO_UDP, | ||
98 | 1, | ||
99 | &sa, | ||
100 | &sa_len, | ||
101 | NULL, NULL, NULL); | ||
102 | GNUNET_NAT_request_reversal (h, | ||
103 | &lsa, | ||
104 | &rsa); | ||
105 | GNUNET_NAT_unregister (h); | ||
106 | } | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Closure for #tcp_send. | ||
111 | */ | ||
112 | struct TcpContext | ||
113 | { | ||
114 | /** | ||
115 | * TCP socket. | ||
116 | */ | ||
117 | struct GNUNET_NETWORK_Handle *s; | ||
118 | |||
119 | /** | ||
120 | * Data to transmit. | ||
121 | */ | ||
122 | uint16_t data; | ||
123 | }; | ||
124 | |||
125 | |||
126 | /** | ||
127 | * Task called by the scheduler once we can do the TCP send | ||
128 | * (or once we failed to connect...). | ||
129 | * | ||
130 | * @param cls the `struct TcpContext` | ||
131 | */ | ||
132 | static void | ||
133 | tcp_send (void *cls) | ||
134 | { | ||
135 | struct TcpContext *ctx = cls; | ||
136 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
137 | |||
138 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
139 | if ((NULL != tc->write_ready) && | ||
140 | (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s))) | ||
141 | { | ||
142 | if (-1 == | ||
143 | GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof(ctx->data))) | ||
144 | { | ||
145 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); | ||
146 | } | ||
147 | GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR); | ||
148 | } | ||
149 | GNUNET_NETWORK_socket_close (ctx->s); | ||
150 | GNUNET_free (ctx); | ||
151 | } | ||
152 | |||
153 | |||
154 | /** | ||
155 | * Try to send @a data to the | ||
156 | * IP @a dst_ipv4' at port @a dport via TCP. | ||
157 | * | ||
158 | * @param dst_ipv4 target IP | ||
159 | * @param dport target port | ||
160 | * @param data data to send | ||
161 | */ | ||
162 | static void | ||
163 | try_send_tcp (uint32_t dst_ipv4, | ||
164 | uint16_t dport, | ||
165 | uint16_t data) | ||
166 | { | ||
167 | struct GNUNET_NETWORK_Handle *s; | ||
168 | struct sockaddr_in sa; | ||
169 | struct TcpContext *ctx; | ||
170 | |||
171 | s = GNUNET_NETWORK_socket_create (AF_INET, | ||
172 | SOCK_STREAM, | ||
173 | 0); | ||
174 | if (NULL == s) | ||
175 | { | ||
176 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
177 | "socket"); | ||
178 | return; | ||
179 | } | ||
180 | memset (&sa, 0, sizeof(sa)); | ||
181 | sa.sin_family = AF_INET; | ||
182 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
183 | sa.sin_len = sizeof(sa); | ||
184 | #endif | ||
185 | sa.sin_addr.s_addr = dst_ipv4; | ||
186 | sa.sin_port = htons (dport); | ||
187 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
188 | "Sending TCP message to `%s'\n", | ||
189 | GNUNET_a2s ((struct sockaddr *) &sa, | ||
190 | sizeof(sa))); | ||
191 | if ((GNUNET_OK != | ||
192 | GNUNET_NETWORK_socket_connect (s, | ||
193 | (const struct sockaddr *) &sa, | ||
194 | sizeof(sa))) && | ||
195 | (errno != EINPROGRESS)) | ||
196 | { | ||
197 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
198 | "connect"); | ||
199 | GNUNET_NETWORK_socket_close (s); | ||
200 | return; | ||
201 | } | ||
202 | ctx = GNUNET_new (struct TcpContext); | ||
203 | ctx->s = s; | ||
204 | ctx->data = data; | ||
205 | GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS, | ||
206 | s, | ||
207 | &tcp_send, | ||
208 | ctx); | ||
209 | } | ||
210 | |||
211 | |||
212 | /** | ||
213 | * Try to send @a data to the | ||
214 | * IP @a dst_ipv4 at port @a dport via UDP. | ||
215 | * | ||
216 | * @param dst_ipv4 target IP | ||
217 | * @param dport target port | ||
218 | * @param data data to send | ||
219 | */ | ||
220 | static void | ||
221 | try_send_udp (uint32_t dst_ipv4, | ||
222 | uint16_t dport, | ||
223 | uint16_t data) | ||
224 | { | ||
225 | struct GNUNET_NETWORK_Handle *s; | ||
226 | struct sockaddr_in sa; | ||
227 | |||
228 | s = GNUNET_NETWORK_socket_create (AF_INET, | ||
229 | SOCK_DGRAM, | ||
230 | 0); | ||
231 | if (NULL == s) | ||
232 | { | ||
233 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
234 | "socket"); | ||
235 | return; | ||
236 | } | ||
237 | memset (&sa, 0, sizeof(sa)); | ||
238 | sa.sin_family = AF_INET; | ||
239 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
240 | sa.sin_len = sizeof(sa); | ||
241 | #endif | ||
242 | sa.sin_addr.s_addr = dst_ipv4; | ||
243 | sa.sin_port = htons (dport); | ||
244 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
245 | "Sending UDP packet to `%s'\n", | ||
246 | GNUNET_a2s ((struct sockaddr *) &sa, | ||
247 | sizeof(sa))); | ||
248 | if (-1 == | ||
249 | GNUNET_NETWORK_socket_sendto (s, | ||
250 | &data, | ||
251 | sizeof(data), | ||
252 | (const struct sockaddr *) &sa, | ||
253 | sizeof(sa))) | ||
254 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
255 | "sendto"); | ||
256 | GNUNET_NETWORK_socket_close (s); | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * We've received a request to probe a NAT | ||
262 | * traversal. Do it. | ||
263 | * | ||
264 | * @param cls handle to client (we always close) | ||
265 | * @param tm message with details about what to test | ||
266 | */ | ||
267 | static void | ||
268 | handle_test (void *cls, | ||
269 | const struct GNUNET_NAT_AUTO_TestMessage *tm) | ||
270 | { | ||
271 | struct ClientData *cd = cls; | ||
272 | uint16_t dport; | ||
273 | |||
274 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
275 | "Received test request\n"); | ||
276 | dport = ntohs (tm->dport); | ||
277 | if (0 == dport) | ||
278 | try_anat (tm->dst_ipv4, | ||
279 | ntohs (tm->data), | ||
280 | (int) ntohl (tm->is_tcp)); | ||
281 | else if (GNUNET_YES == ntohl (tm->is_tcp)) | ||
282 | try_send_tcp (tm->dst_ipv4, | ||
283 | dport, | ||
284 | tm->data); | ||
285 | else | ||
286 | try_send_udp (tm->dst_ipv4, | ||
287 | dport, | ||
288 | tm->data); | ||
289 | GNUNET_SERVICE_client_drop (cd->client); | ||
290 | } | ||
291 | |||
292 | |||
293 | /** | ||
294 | * Main function that will be run. | ||
295 | * | ||
296 | * @param cls closure | ||
297 | * @param c configuration | ||
298 | * @param srv service handle | ||
299 | */ | ||
300 | static void | ||
301 | run (void *cls, | ||
302 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
303 | struct GNUNET_SERVICE_Handle *srv) | ||
304 | { | ||
305 | cfg = c; | ||
306 | } | ||
307 | |||
308 | |||
309 | /** | ||
310 | * Forcefully drops client after 1s. | ||
311 | * | ||
312 | * @param cls our `struct ClientData` of a client to drop | ||
313 | */ | ||
314 | static void | ||
315 | force_timeout (void *cls) | ||
316 | { | ||
317 | struct ClientData *cd = cls; | ||
318 | |||
319 | cd->tt = NULL; | ||
320 | GNUNET_SERVICE_client_drop (cd->client); | ||
321 | } | ||
322 | |||
323 | |||
324 | /** | ||
325 | * Callback called when a client connects to the service. | ||
326 | * | ||
327 | * @param cls closure for the service | ||
328 | * @param c the new client that connected to the service | ||
329 | * @param mq the message queue used to send messages to the client | ||
330 | * @return our `struct ClientData` | ||
331 | */ | ||
332 | static void * | ||
333 | client_connect_cb (void *cls, | ||
334 | struct GNUNET_SERVICE_Client *c, | ||
335 | struct GNUNET_MQ_Handle *mq) | ||
336 | { | ||
337 | struct ClientData *cd; | ||
338 | |||
339 | cd = GNUNET_new (struct ClientData); | ||
340 | cd->client = c; | ||
341 | cd->tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
342 | &force_timeout, | ||
343 | cd); | ||
344 | return cd; | ||
345 | } | ||
346 | |||
347 | |||
348 | /** | ||
349 | * Callback called when a client disconnected from the service | ||
350 | * | ||
351 | * @param cls closure for the service | ||
352 | * @param c the client that disconnected | ||
353 | * @param internal_cls our `struct ClientData` | ||
354 | */ | ||
355 | static void | ||
356 | client_disconnect_cb (void *cls, | ||
357 | struct GNUNET_SERVICE_Client *c, | ||
358 | void *internal_cls) | ||
359 | { | ||
360 | struct ClientData *cd = internal_cls; | ||
361 | |||
362 | if (NULL != cd->tt) | ||
363 | GNUNET_SCHEDULER_cancel (cd->tt); | ||
364 | GNUNET_free (cd); | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Define "main" method using service macro. | ||
370 | */ | ||
371 | GNUNET_SERVICE_MAIN | ||
372 | ("nat-server", | ||
373 | GNUNET_SERVICE_OPTION_NONE, | ||
374 | &run, | ||
375 | &client_connect_cb, | ||
376 | &client_disconnect_cb, | ||
377 | NULL, | ||
378 | GNUNET_MQ_hd_fixed_size (test, | ||
379 | GNUNET_MESSAGE_TYPE_NAT_TEST, | ||
380 | struct GNUNET_NAT_AUTO_TestMessage, | ||
381 | NULL), | ||
382 | GNUNET_MQ_handler_end ()); | ||
383 | |||
384 | |||
385 | #if defined(__linux__) && defined(__GLIBC__) | ||
386 | #include <malloc.h> | ||
387 | |||
388 | /** | ||
389 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
390 | */ | ||
391 | void __attribute__ ((constructor)) | ||
392 | GNUNET_ARM_memory_init () | ||
393 | { | ||
394 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
395 | mallopt (M_TOP_PAD, 1 * 1024); | ||
396 | malloc_trim (0); | ||
397 | } | ||
398 | |||
399 | |||
400 | #endif | ||
401 | |||
402 | |||
403 | /* end of gnunet-nat-server.c */ | ||
diff --git a/src/cli/nat-auto/meson.build b/src/cli/nat-auto/meson.build new file mode 100644 index 000000000..69e719126 --- /dev/null +++ b/src/cli/nat-auto/meson.build | |||
@@ -0,0 +1,14 @@ | |||
1 | executable ('gnunet-nat-auto', | ||
2 | ['gnunet-nat-auto.c'], | ||
3 | dependencies: [libgnunetnatauto_dep, libgnunetutil_dep, | ||
4 | libgnunetnat_dep], | ||
5 | include_directories: [incdir, configuration_inc], | ||
6 | install: true, | ||
7 | install_dir: get_option('bindir')) | ||
8 | executable ('gnunet-nat-server', | ||
9 | ['gnunet-nat-server.c'], | ||
10 | dependencies: [libgnunetnatauto_dep, libgnunetutil_dep, libgnunetnat_dep], | ||
11 | include_directories: [incdir, configuration_inc], | ||
12 | install: true, | ||
13 | install_dir: get_option('bindir')) | ||
14 | |||
diff --git a/src/cli/nat/.gitignore b/src/cli/nat/.gitignore new file mode 100644 index 000000000..89e635e9f --- /dev/null +++ b/src/cli/nat/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-nat | |||
diff --git a/src/cli/nat/Makefile.am b/src/cli/nat/Makefile.am new file mode 100644 index 000000000..1bdd1a6be --- /dev/null +++ b/src/cli/nat/Makefile.am | |||
@@ -0,0 +1,17 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | libexecdir= $(pkglibdir)/libexec/ | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | bin_PROGRAMS = \ | ||
9 | gnunet-nat | ||
10 | |||
11 | gnunet_nat_SOURCES = \ | ||
12 | gnunet-nat.c | ||
13 | gnunet_nat_LDADD = \ | ||
14 | $(top_builddir)/src/service/nat/libgnunetnatnew.la \ | ||
15 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
16 | gnunet_nat_LDFLAGS = \ | ||
17 | $(GN_LIBINTL) | ||
diff --git a/src/cli/nat/gnunet-nat.c b/src/cli/nat/gnunet-nat.c new file mode 100644 index 000000000..fd85549d6 --- /dev/null +++ b/src/cli/nat/gnunet-nat.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2015, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/nat/gnunet-nat.c | ||
23 | * @brief Command-line tool to interact with the NAT service | ||
24 | * @author Christian Grothoff | ||
25 | * @author Bruno Cabral | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_nat_service.h" | ||
30 | |||
31 | /** | ||
32 | * Value to return from #main(). | ||
33 | */ | ||
34 | static int global_ret; | ||
35 | |||
36 | /** | ||
37 | * Name of section in configuration file to use for | ||
38 | * additional options. | ||
39 | */ | ||
40 | static char *section_name; | ||
41 | |||
42 | /** | ||
43 | * Flag set to 1 if we use IPPROTO_UDP. | ||
44 | */ | ||
45 | static int use_udp; | ||
46 | |||
47 | /** | ||
48 | * Flag set to 1 if we are to listen for connection reversal requests. | ||
49 | */ | ||
50 | static int listen_reversal; | ||
51 | |||
52 | /** | ||
53 | * Flag set to 1 if we use IPPROTO_TCP. | ||
54 | */ | ||
55 | static int use_tcp; | ||
56 | |||
57 | /** | ||
58 | * Protocol to use. | ||
59 | */ | ||
60 | static uint8_t proto; | ||
61 | |||
62 | /** | ||
63 | * Local address to use for connection reversal request. | ||
64 | */ | ||
65 | static char *local_addr; | ||
66 | |||
67 | /** | ||
68 | * Remote address to use for connection reversal request. | ||
69 | */ | ||
70 | static char *remote_addr; | ||
71 | |||
72 | /** | ||
73 | * Should we actually bind to #bind_addr and receive and process STUN requests? | ||
74 | */ | ||
75 | static int do_stun; | ||
76 | |||
77 | /** | ||
78 | * Handle to NAT operation. | ||
79 | */ | ||
80 | static struct GNUNET_NAT_Handle *nh; | ||
81 | |||
82 | /** | ||
83 | * Listen socket for STUN processing. | ||
84 | */ | ||
85 | static struct GNUNET_NETWORK_Handle *ls; | ||
86 | |||
87 | /** | ||
88 | * Task for reading STUN packets. | ||
89 | */ | ||
90 | static struct GNUNET_SCHEDULER_Task *rtask; | ||
91 | |||
92 | |||
93 | /** | ||
94 | * Test if all activities have finished, and if so, | ||
95 | * terminate. | ||
96 | */ | ||
97 | static void | ||
98 | test_finished () | ||
99 | { | ||
100 | if (NULL != nh) | ||
101 | return; | ||
102 | if (NULL != rtask) | ||
103 | return; | ||
104 | GNUNET_SCHEDULER_shutdown (); | ||
105 | } | ||
106 | |||
107 | |||
108 | /** | ||
109 | * Signature of the callback passed to #GNUNET_NAT_register() for | ||
110 | * a function to call whenever our set of 'valid' addresses changes. | ||
111 | * | ||
112 | * @param cls closure, NULL | ||
113 | * @param[in,out] app_ctx location where the app can store stuff | ||
114 | * on add and retrieve it on remove | ||
115 | * @param add_remove #GNUNET_YES to add a new public IP address, | ||
116 | * #GNUNET_NO to remove a previous (now invalid) one | ||
117 | * @param ac address class the address belongs to | ||
118 | * @param addr either the previous or the new public IP address | ||
119 | * @param addrlen actual length of the @a addr | ||
120 | */ | ||
121 | static void | ||
122 | address_cb (void *cls, | ||
123 | void **app_ctx, | ||
124 | int add_remove, | ||
125 | enum GNUNET_NAT_AddressClass ac, | ||
126 | const struct sockaddr *addr, | ||
127 | socklen_t addrlen) | ||
128 | { | ||
129 | (void) cls; | ||
130 | (void) app_ctx; | ||
131 | |||
132 | fprintf (stdout, | ||
133 | "%s %s (%d)\n", | ||
134 | add_remove ? "+" : "-", | ||
135 | GNUNET_a2s (addr, addrlen), | ||
136 | (int) ac); | ||
137 | } | ||
138 | |||
139 | |||
140 | /** | ||
141 | * Signature of the callback passed to #GNUNET_NAT_register(). | ||
142 | * for a function to call whenever someone asks us to do connection | ||
143 | * reversal. | ||
144 | * | ||
145 | * @param cls closure, NULL | ||
146 | * @param remote_addr public IP address of the other peer | ||
147 | * @param remote_addrlen actual length of the @a remote_addr | ||
148 | */ | ||
149 | static void | ||
150 | reversal_cb (void *cls, | ||
151 | const struct sockaddr *remote_addr, | ||
152 | socklen_t remote_addrlen) | ||
153 | { | ||
154 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
155 | "Connection reversal requested by %s\n", | ||
156 | GNUNET_a2s (remote_addr, remote_addrlen)); | ||
157 | } | ||
158 | |||
159 | |||
160 | /** | ||
161 | * Task run on shutdown. | ||
162 | * | ||
163 | * @param cls NULL | ||
164 | */ | ||
165 | static void | ||
166 | do_shutdown (void *cls) | ||
167 | { | ||
168 | if (NULL != nh) | ||
169 | { | ||
170 | GNUNET_NAT_unregister (nh); | ||
171 | nh = NULL; | ||
172 | } | ||
173 | if (NULL != ls) | ||
174 | { | ||
175 | GNUNET_NETWORK_socket_close (ls); | ||
176 | ls = NULL; | ||
177 | } | ||
178 | if (NULL != rtask) | ||
179 | { | ||
180 | GNUNET_SCHEDULER_cancel (rtask); | ||
181 | rtask = NULL; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | |||
186 | /** | ||
187 | * Task to receive incoming packets for STUN processing. | ||
188 | */ | ||
189 | static void | ||
190 | stun_read_task (void *cls) | ||
191 | { | ||
192 | ssize_t size; | ||
193 | |||
194 | rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
195 | ls, | ||
196 | &stun_read_task, | ||
197 | NULL); | ||
198 | size = GNUNET_NETWORK_socket_recvfrom_amount (ls); | ||
199 | if (size > 0) | ||
200 | { | ||
201 | GNUNET_break (0); | ||
202 | GNUNET_SCHEDULER_shutdown (); | ||
203 | global_ret = 1; | ||
204 | return; | ||
205 | } | ||
206 | { | ||
207 | char buf[size + 1]; | ||
208 | struct sockaddr_storage sa; | ||
209 | socklen_t salen = sizeof(sa); | ||
210 | ssize_t ret; | ||
211 | |||
212 | ret = GNUNET_NETWORK_socket_recvfrom (ls, | ||
213 | buf, | ||
214 | size + 1, | ||
215 | (struct sockaddr *) &sa, | ||
216 | &salen); | ||
217 | if (ret != size) | ||
218 | { | ||
219 | GNUNET_break (0); | ||
220 | GNUNET_SCHEDULER_shutdown (); | ||
221 | global_ret = 1; | ||
222 | return; | ||
223 | } | ||
224 | (void) GNUNET_NAT_stun_handle_packet (nh, | ||
225 | (const struct sockaddr *) &sa, | ||
226 | salen, | ||
227 | buf, | ||
228 | ret); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | |||
233 | /** | ||
234 | * Main function that will be run. | ||
235 | * | ||
236 | * @param cls closure | ||
237 | * @param args remaining command-line arguments | ||
238 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
239 | * @param c configuration | ||
240 | */ | ||
241 | static void | ||
242 | run (void *cls, | ||
243 | char *const *args, | ||
244 | const char *cfgfile, | ||
245 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
246 | { | ||
247 | uint8_t af; | ||
248 | struct sockaddr *local_sa; | ||
249 | struct sockaddr *remote_sa; | ||
250 | socklen_t local_len; | ||
251 | size_t remote_len; | ||
252 | |||
253 | if (use_tcp && use_udp) | ||
254 | { | ||
255 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); | ||
256 | global_ret = 1; | ||
257 | return; | ||
258 | } | ||
259 | proto = 0; | ||
260 | if (use_tcp) | ||
261 | proto = IPPROTO_TCP; | ||
262 | if (use_udp) | ||
263 | proto = IPPROTO_UDP; | ||
264 | |||
265 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
266 | |||
267 | if (0 == proto) | ||
268 | { | ||
269 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n"); | ||
270 | global_ret = 1; | ||
271 | return; | ||
272 | } | ||
273 | local_len = 0; | ||
274 | local_sa = NULL; | ||
275 | remote_len = 0; | ||
276 | remote_sa = NULL; | ||
277 | if (NULL != local_addr) | ||
278 | { | ||
279 | local_len = | ||
280 | (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa); | ||
281 | if (0 == local_len) | ||
282 | { | ||
283 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
284 | "Invalid socket address `%s'\n", | ||
285 | local_addr); | ||
286 | goto fail_and_shutdown; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | if (NULL != remote_addr) | ||
291 | { | ||
292 | remote_len = | ||
293 | GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa); | ||
294 | if (0 == remote_len) | ||
295 | { | ||
296 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
297 | "Invalid socket address `%s'\n", | ||
298 | remote_addr); | ||
299 | goto fail_and_shutdown; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | if (NULL != local_addr) | ||
304 | { | ||
305 | if (NULL == section_name) | ||
306 | section_name = GNUNET_strdup ("undefined"); | ||
307 | nh = GNUNET_NAT_register (c, | ||
308 | section_name, | ||
309 | proto, | ||
310 | 1, | ||
311 | (const struct sockaddr **) &local_sa, | ||
312 | &local_len, | ||
313 | &address_cb, | ||
314 | (listen_reversal) ? &reversal_cb : NULL, | ||
315 | NULL); | ||
316 | } | ||
317 | else if (listen_reversal) | ||
318 | { | ||
319 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
320 | "Use of `-W` only effective in combination with `-i`\n"); | ||
321 | goto fail_and_shutdown; | ||
322 | } | ||
323 | |||
324 | if (NULL != remote_addr) | ||
325 | { | ||
326 | int ret; | ||
327 | |||
328 | if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len)) | ||
329 | { | ||
330 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
331 | "Require IPv4 local address to initiate connection reversal\n"); | ||
332 | goto fail_and_shutdown; | ||
333 | } | ||
334 | if (sizeof(struct sockaddr_in) != remote_len) | ||
335 | { | ||
336 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
337 | "Require IPv4 reversal target address\n"); | ||
338 | goto fail_and_shutdown; | ||
339 | } | ||
340 | GNUNET_assert (AF_INET == local_sa->sa_family); | ||
341 | GNUNET_assert (AF_INET == remote_sa->sa_family); | ||
342 | ret = GNUNET_NAT_request_reversal (nh, | ||
343 | (const struct sockaddr_in *) local_sa, | ||
344 | (const struct sockaddr_in *) remote_sa); | ||
345 | switch (ret) | ||
346 | { | ||
347 | case GNUNET_SYSERR: | ||
348 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
349 | "Connection reversal internal error\n"); | ||
350 | break; | ||
351 | |||
352 | case GNUNET_NO: | ||
353 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
354 | "Connection reversal unavailable\n"); | ||
355 | break; | ||
356 | |||
357 | case GNUNET_OK: | ||
358 | /* operation in progress */ | ||
359 | break; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (do_stun) | ||
364 | { | ||
365 | if (NULL == local_addr) | ||
366 | { | ||
367 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
368 | "Require local address to support STUN requests\n"); | ||
369 | goto fail_and_shutdown; | ||
370 | } | ||
371 | if (IPPROTO_UDP != proto) | ||
372 | { | ||
373 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n"); | ||
374 | goto fail_and_shutdown; | ||
375 | } | ||
376 | ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP); | ||
377 | if (NULL == ls) | ||
378 | { | ||
379 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n"); | ||
380 | goto fail_and_shutdown; | ||
381 | } | ||
382 | if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len)) | ||
383 | { | ||
384 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
385 | "Failed to bind to %s: %s\n", | ||
386 | GNUNET_a2s (local_sa, local_len), | ||
387 | strerror (errno)); | ||
388 | goto fail_and_shutdown; | ||
389 | } | ||
390 | rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
391 | ls, | ||
392 | &stun_read_task, | ||
393 | NULL); | ||
394 | } | ||
395 | GNUNET_free (remote_sa); | ||
396 | GNUNET_free (local_sa); | ||
397 | test_finished (); | ||
398 | return; | ||
399 | fail_and_shutdown: | ||
400 | global_ret = 1; | ||
401 | GNUNET_SCHEDULER_shutdown (); | ||
402 | GNUNET_free (remote_sa); | ||
403 | GNUNET_free (local_sa); | ||
404 | } | ||
405 | |||
406 | |||
407 | /** | ||
408 | * Main function of gnunet-nat | ||
409 | * | ||
410 | * @param argc number of command-line arguments | ||
411 | * @param argv command line | ||
412 | * @return 0 on success, -1 on error | ||
413 | */ | ||
414 | int | ||
415 | main (int argc, char *const argv[]) | ||
416 | { | ||
417 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
418 | GNUNET_GETOPT_option_string ( | ||
419 | 'i', | ||
420 | "in", | ||
421 | "ADDRESS", | ||
422 | gettext_noop ("which IP and port are we locally using to bind/listen to"), | ||
423 | &local_addr), | ||
424 | |||
425 | GNUNET_GETOPT_option_string ( | ||
426 | 'r', | ||
427 | "remote", | ||
428 | "ADDRESS", | ||
429 | gettext_noop ( | ||
430 | "which remote IP and port should be asked for connection reversal"), | ||
431 | &remote_addr), | ||
432 | |||
433 | GNUNET_GETOPT_option_string ( | ||
434 | 'S', | ||
435 | "section", | ||
436 | NULL, | ||
437 | gettext_noop ( | ||
438 | "name of configuration section to find additional options, such as manual host punching data"), | ||
439 | §ion_name), | ||
440 | |||
441 | GNUNET_GETOPT_option_flag ('s', | ||
442 | "stun", | ||
443 | gettext_noop ("enable STUN processing"), | ||
444 | &do_stun), | ||
445 | |||
446 | GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp), | ||
447 | |||
448 | GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp), | ||
449 | |||
450 | GNUNET_GETOPT_option_flag ('W', | ||
451 | "watch", | ||
452 | gettext_noop ( | ||
453 | "watch for connection reversal requests"), | ||
454 | &listen_reversal), | ||
455 | GNUNET_GETOPT_OPTION_END | ||
456 | }; | ||
457 | |||
458 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
459 | return 2; | ||
460 | if (GNUNET_OK != | ||
461 | GNUNET_PROGRAM_run (argc, | ||
462 | argv, | ||
463 | "gnunet-nat [options]", | ||
464 | _ ("GNUnet NAT traversal autoconfigure daemon"), | ||
465 | options, | ||
466 | &run, | ||
467 | NULL)) | ||
468 | { | ||
469 | global_ret = 1; | ||
470 | } | ||
471 | GNUNET_free_nz ((void *) argv); | ||
472 | return global_ret; | ||
473 | } | ||
474 | |||
475 | |||
476 | /* end of gnunet-nat.c */ | ||
diff --git a/src/cli/nat/meson.build b/src/cli/nat/meson.build new file mode 100644 index 000000000..41af0c087 --- /dev/null +++ b/src/cli/nat/meson.build | |||
@@ -0,0 +1,7 @@ | |||
1 | executable ('gnunet-nat', | ||
2 | ['gnunet-nat.c'], | ||
3 | dependencies: [libgnunetnat_dep, libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
7 | |||
diff --git a/src/cli/nse/.gitignore b/src/cli/nse/.gitignore new file mode 100644 index 000000000..ec3c6a9cf --- /dev/null +++ b/src/cli/nse/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-nse | |||
diff --git a/src/cli/nse/Makefile.am b/src/cli/nse/Makefile.am new file mode 100644 index 000000000..e724a22d3 --- /dev/null +++ b/src/cli/nse/Makefile.am | |||
@@ -0,0 +1,19 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | bin_PROGRAMS = gnunet-nse | ||
14 | |||
15 | gnunet_nse_SOURCES = gnunet-nse.c | ||
16 | gnunet_nse_LDADD = \ | ||
17 | $(top_builddir)/src/service/nse/libgnunetnse.la \ | ||
18 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
19 | $(XLIB) $(GN_LIBINTL) | ||
diff --git a/src/cli/nse/gnunet-nse.c b/src/cli/nse/gnunet-nse.c new file mode 100644 index 000000000..b88697695 --- /dev/null +++ b/src/cli/nse/gnunet-nse.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2008--2014, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file nse/gnunet-nse.c | ||
23 | * @brief Program to display network size estimates from the NSE service | ||
24 | * @author Sree Harsha Totakura <sreeharsha@totakura.in> | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_nse_service.h" | ||
29 | #include <gnunet_util_lib.h> | ||
30 | |||
31 | /** | ||
32 | * The handle to the NSE service | ||
33 | */ | ||
34 | static struct GNUNET_NSE_Handle *nse; | ||
35 | |||
36 | /** | ||
37 | * The program status; 0 for success. | ||
38 | */ | ||
39 | static int status; | ||
40 | |||
41 | /** | ||
42 | * Monitor flag. | ||
43 | */ | ||
44 | static int monitor; | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Task to shutdown and clean up all state | ||
49 | * | ||
50 | * @param cls NULL | ||
51 | */ | ||
52 | static void | ||
53 | do_shutdown (void *cls) | ||
54 | { | ||
55 | (void) cls; | ||
56 | if (NULL != nse) | ||
57 | { | ||
58 | GNUNET_NSE_disconnect (nse); | ||
59 | nse = NULL; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | |||
64 | /** | ||
65 | * Callback to call when network size estimate is updated. | ||
66 | * | ||
67 | * @param cls NULL | ||
68 | * @param timestamp server timestamp | ||
69 | * @param estimate the value of the current network size estimate | ||
70 | * @param std_dev standard deviation (rounded down to nearest integer) | ||
71 | * of the size estimation values seen | ||
72 | */ | ||
73 | static void | ||
74 | handle_estimate (void *cls, | ||
75 | struct GNUNET_TIME_Absolute timestamp, | ||
76 | double estimate, | ||
77 | double std_dev) | ||
78 | { | ||
79 | (void) cls; | ||
80 | status = 0; | ||
81 | fprintf (stdout, | ||
82 | "%s: #peers=%f (ld(#peers)=%f, stddev=%f)\n", | ||
83 | GNUNET_STRINGS_absolute_time_to_string (timestamp), | ||
84 | GNUNET_NSE_log_estimate_to_n (estimate), | ||
85 | estimate, | ||
86 | std_dev); | ||
87 | if (! monitor) | ||
88 | GNUNET_SCHEDULER_shutdown (); | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Actual main function that runs the emulation. | ||
94 | * | ||
95 | * @param cls unused | ||
96 | * @param args remaining args, unused | ||
97 | * @param cfgfile name of the configuration | ||
98 | * @param cfg configuration handle | ||
99 | */ | ||
100 | static void | ||
101 | run (void *cls, | ||
102 | char *const *args, | ||
103 | const char *cfgfile, | ||
104 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
105 | { | ||
106 | (void) cls; | ||
107 | (void) args; | ||
108 | (void) cfgfile; | ||
109 | nse = GNUNET_NSE_connect (cfg, &handle_estimate, NULL); | ||
110 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
111 | } | ||
112 | |||
113 | |||
114 | /** | ||
115 | * Main function. | ||
116 | * | ||
117 | * @return 0 on success | ||
118 | */ | ||
119 | int | ||
120 | main (int argc, char *const *argv) | ||
121 | { | ||
122 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
123 | GNUNET_GETOPT_option_flag ('m', | ||
124 | "monitor", | ||
125 | gettext_noop ( | ||
126 | "Monitor and output current estimates"), | ||
127 | &monitor), | ||
128 | GNUNET_GETOPT_OPTION_END | ||
129 | }; | ||
130 | |||
131 | status = 1; | ||
132 | if (GNUNET_OK != | ||
133 | GNUNET_PROGRAM_run (argc, | ||
134 | argv, | ||
135 | "gnunet-nse", | ||
136 | gettext_noop ( | ||
137 | "Show network size estimates from NSE service."), | ||
138 | options, | ||
139 | &run, | ||
140 | NULL)) | ||
141 | return 2; | ||
142 | return status; | ||
143 | } | ||
diff --git a/src/cli/nse/meson.build b/src/cli/nse/meson.build new file mode 100644 index 000000000..584c7eb71 --- /dev/null +++ b/src/cli/nse/meson.build | |||
@@ -0,0 +1,7 @@ | |||
1 | executable ('gnunet-nse', | ||
2 | ['gnunet-nse.c'], | ||
3 | dependencies: [libgnunetnse_dep, m_dep, libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
7 | |||
diff --git a/src/cli/peerstore/.gitignore b/src/cli/peerstore/.gitignore new file mode 100644 index 000000000..3ae0f6c6c --- /dev/null +++ b/src/cli/peerstore/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-peerstore | |||
diff --git a/src/cli/peerstore/Makefile.am b/src/cli/peerstore/Makefile.am new file mode 100644 index 000000000..f5be82f09 --- /dev/null +++ b/src/cli/peerstore/Makefile.am | |||
@@ -0,0 +1,23 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | if USE_COVERAGE | ||
11 | AM_CFLAGS = -fprofile-arcs -ftest-coverage | ||
12 | endif | ||
13 | |||
14 | # This program does not do anything. | ||
15 | noinst_PROGRAMS = \ | ||
16 | gnunet-peerstore | ||
17 | |||
18 | gnunet_peerstore_SOURCES = \ | ||
19 | gnunet-peerstore.c | ||
20 | gnunet_peerstore_LDADD = \ | ||
21 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
22 | $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ | ||
23 | $(GN_LIBINTL) | ||
diff --git a/src/cli/peerstore/gnunet-peerstore.c b/src/cli/peerstore/gnunet-peerstore.c new file mode 100644 index 000000000..84cae675f --- /dev/null +++ b/src/cli/peerstore/gnunet-peerstore.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file peerstore/gnunet-peerstore.c | ||
23 | * @brief peerstore tool | ||
24 | * @author Omar Tarabai | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_peerstore_service.h" | ||
29 | |||
30 | static int ret; | ||
31 | |||
32 | /* | ||
33 | * Handle to PEERSTORE service | ||
34 | */ | ||
35 | static struct GNUNET_PEERSTORE_Handle *peerstore_handle; | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Run on shutdown | ||
40 | * | ||
41 | * @param cls unused | ||
42 | */ | ||
43 | static void | ||
44 | shutdown_task (void *cls) | ||
45 | { | ||
46 | if (NULL != peerstore_handle) | ||
47 | { | ||
48 | GNUNET_PEERSTORE_disconnect (peerstore_handle); | ||
49 | peerstore_handle = NULL; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Main function that will be run by the scheduler. | ||
56 | * | ||
57 | * @param cls closure | ||
58 | * @param args remaining command-line arguments | ||
59 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
60 | * @param cfg configuration | ||
61 | */ | ||
62 | static void | ||
63 | run (void *cls, | ||
64 | char *const *args, | ||
65 | const char *cfgfile, | ||
66 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
67 | { | ||
68 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
69 | NULL); | ||
70 | peerstore_handle = GNUNET_PEERSTORE_connect (cfg); | ||
71 | GNUNET_assert (NULL != peerstore_handle); | ||
72 | ret = 0; | ||
73 | } | ||
74 | |||
75 | |||
76 | /** | ||
77 | * The main function to peerstore. | ||
78 | * | ||
79 | * @param argc number of arguments from the command line | ||
80 | * @param argv command line arguments | ||
81 | * @return 0 ok, 1 on error | ||
82 | */ | ||
83 | int | ||
84 | main (int argc, char *const *argv) | ||
85 | { | ||
86 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
87 | GNUNET_GETOPT_OPTION_END | ||
88 | }; | ||
89 | |||
90 | return (GNUNET_OK == | ||
91 | GNUNET_PROGRAM_run (argc, argv, "gnunet-peerstore [options [value]]", | ||
92 | gettext_noop ("peerstore"), options, &run, | ||
93 | NULL)) ? ret : 1; | ||
94 | } | ||
95 | |||
96 | |||
97 | /* end of gnunet-peerstore.c */ | ||
diff --git a/src/cli/peerstore/meson.build b/src/cli/peerstore/meson.build new file mode 100644 index 000000000..1fd177734 --- /dev/null +++ b/src/cli/peerstore/meson.build | |||
@@ -0,0 +1,9 @@ | |||
1 | executable ('gnunet-peerstore', | ||
2 | 'gnunet-peerstore.c', | ||
3 | dependencies: [libgnunetpeerstore_dep, | ||
4 | libgnunetutil_dep | ||
5 | ], | ||
6 | include_directories: [incdir, configuration_inc], | ||
7 | install: false, | ||
8 | install_dir: get_option('bindir')) | ||
9 | |||
diff --git a/src/cli/reclaim/.gitignore b/src/cli/reclaim/.gitignore new file mode 100644 index 000000000..49e84eb66 --- /dev/null +++ b/src/cli/reclaim/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-reclaim | ||
2 | gnunet-did | ||
diff --git a/src/cli/reclaim/Makefile.am b/src/cli/reclaim/Makefile.am new file mode 100644 index 000000000..9803ac7ac --- /dev/null +++ b/src/cli/reclaim/Makefile.am | |||
@@ -0,0 +1,57 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS = --coverage -O0 | ||
8 | XLIB = -lgcov | ||
9 | endif | ||
10 | |||
11 | |||
12 | EXTRA_DIST = \ | ||
13 | test_reclaim_defaults.conf \ | ||
14 | test_reclaim.conf \ | ||
15 | $(check_SCRIPTS) | ||
16 | |||
17 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
18 | |||
19 | libexecdir= $(pkglibdir)/libexec/ | ||
20 | |||
21 | bin_PROGRAMS = \ | ||
22 | gnunet-reclaim \ | ||
23 | gnunet-did | ||
24 | |||
25 | gnunet_reclaim_SOURCES = \ | ||
26 | gnunet-reclaim.c | ||
27 | gnunet_reclaim_LDADD = \ | ||
28 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
29 | $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ | ||
30 | $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \ | ||
31 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
32 | $(GN_LIBINTL) | ||
33 | |||
34 | gnunet_did_SOURCES = \ | ||
35 | gnunet-did.c | ||
36 | gnunet_did_LDADD = \ | ||
37 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
38 | $(top_builddir)/src/service/gns/libgnunetgns.la \ | ||
39 | $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ | ||
40 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
41 | $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ | ||
42 | $(top_builddir)/src/service/reclaim/libgnunetdid.la \ | ||
43 | -ljansson | ||
44 | gnunet_did_CFLAGS = \ | ||
45 | -I$(top_builddir)/src/service/reclaim | ||
46 | |||
47 | check_SCRIPTS = \ | ||
48 | test_reclaim_attribute.sh \ | ||
49 | test_reclaim_issue.sh \ | ||
50 | test_reclaim_consume.sh | ||
51 | |||
52 | if ENABLE_TEST_RUN | ||
53 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
54 | TESTS = \ | ||
55 | $(check_SCRIPTS) \ | ||
56 | $(check_PROGRAMS) | ||
57 | endif | ||
diff --git a/src/cli/reclaim/gnunet-did.c b/src/cli/reclaim/gnunet-did.c new file mode 100644 index 000000000..a8ac7652e --- /dev/null +++ b/src/cli/reclaim/gnunet-did.c | |||
@@ -0,0 +1,661 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2022 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * FIXME: Do we only want to handle EdDSA identities? | ||
23 | * TODO: Own GNS record type | ||
24 | * TODO: Fix overwrite of records in @ if present look for other with same sub | ||
25 | * TODO. Tests | ||
26 | * TODO: Move constants to did.h | ||
27 | * FIXME: Remove and lookup require differnt representations (did vs egoname) | ||
28 | */ | ||
29 | |||
30 | /** | ||
31 | * @author Tristan Schwieren | ||
32 | * @file src/did/gnunet-did.c | ||
33 | * @brief DID Method Wrapper | ||
34 | */ | ||
35 | #include "platform.h" | ||
36 | #include "gnunet_util_lib.h" | ||
37 | #include "gnunet_namestore_service.h" | ||
38 | #include "gnunet_identity_service.h" | ||
39 | #include "gnunet_gns_service.h" | ||
40 | #include "gnunet_gnsrecord_lib.h" | ||
41 | #include "did_core.h" | ||
42 | |||
43 | #define GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME "1d" | ||
44 | |||
45 | /** | ||
46 | * return value | ||
47 | */ | ||
48 | static int ret; | ||
49 | |||
50 | /** | ||
51 | * Replace DID Document Flag | ||
52 | */ | ||
53 | static int replace; | ||
54 | |||
55 | /** | ||
56 | * Remove DID Document Flag | ||
57 | */ | ||
58 | static int remove_did; | ||
59 | |||
60 | /** | ||
61 | * Get DID Documement for DID Flag | ||
62 | */ | ||
63 | static int get; | ||
64 | |||
65 | /** | ||
66 | * Create DID Document Flag | ||
67 | */ | ||
68 | static int create; | ||
69 | |||
70 | /** | ||
71 | * Show DID for Ego Flag | ||
72 | */ | ||
73 | static int show; | ||
74 | |||
75 | /** | ||
76 | * Show DID for Ego Flag | ||
77 | */ | ||
78 | static int show_all; | ||
79 | |||
80 | /** | ||
81 | * DID Attribut String | ||
82 | */ | ||
83 | static char *did; | ||
84 | |||
85 | /** | ||
86 | * DID Document Attribut String | ||
87 | */ | ||
88 | static char *didd; | ||
89 | |||
90 | /** | ||
91 | * Ego Attribut String | ||
92 | */ | ||
93 | static char *egoname; | ||
94 | |||
95 | /** | ||
96 | * DID Document expiration Date Attribut String | ||
97 | */ | ||
98 | static char *expire; | ||
99 | |||
100 | /* | ||
101 | * Handle to the GNS service | ||
102 | */ | ||
103 | static struct GNUNET_GNS_Handle *gns_handle; | ||
104 | |||
105 | /* | ||
106 | * Handle to the NAMESTORE service | ||
107 | */ | ||
108 | static struct GNUNET_NAMESTORE_Handle *namestore_handle; | ||
109 | |||
110 | /* | ||
111 | * Handle to the IDENTITY service | ||
112 | */ | ||
113 | static struct GNUNET_IDENTITY_Handle *identity_handle; | ||
114 | |||
115 | |||
116 | /* | ||
117 | * The configuration | ||
118 | */ | ||
119 | const static struct GNUNET_CONFIGURATION_Handle *my_cfg; | ||
120 | |||
121 | /** | ||
122 | * Give ego exists | ||
123 | */ | ||
124 | static int ego_exists = 0; | ||
125 | |||
126 | /** | ||
127 | * @brief Disconnect and shutdown | ||
128 | * @param cls closure | ||
129 | */ | ||
130 | static void | ||
131 | cleanup (void *cls) | ||
132 | { | ||
133 | if (NULL != gns_handle) | ||
134 | GNUNET_GNS_disconnect (gns_handle); | ||
135 | if (NULL != namestore_handle) | ||
136 | GNUNET_NAMESTORE_disconnect (namestore_handle); | ||
137 | if (NULL != identity_handle) | ||
138 | GNUNET_IDENTITY_disconnect (identity_handle); | ||
139 | |||
140 | GNUNET_free (did); | ||
141 | GNUNET_free (didd); | ||
142 | GNUNET_free (egoname); | ||
143 | GNUNET_free (expire); | ||
144 | |||
145 | GNUNET_SCHEDULER_shutdown (); | ||
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * @brief GNS lookup callback. Prints the DID Document to standard out. | ||
151 | * Fails if there is more than one DID record. | ||
152 | * | ||
153 | * @param cls closure | ||
154 | * @param rd_count number of records in @a rd | ||
155 | * @param rd the records in the reply | ||
156 | */ | ||
157 | static void | ||
158 | print_did_document ( | ||
159 | enum GNUNET_GenericReturnValue status, | ||
160 | char *did_document, | ||
161 | void *cls | ||
162 | ) | ||
163 | { | ||
164 | if (GNUNET_OK == status) | ||
165 | printf ("%s\n", did_document); | ||
166 | else | ||
167 | printf ("An error occured: %s\n", did_document); | ||
168 | |||
169 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
170 | ret = 0; | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | |||
175 | /** | ||
176 | * @brief Resolve a DID given by the user. | ||
177 | */ | ||
178 | static void | ||
179 | resolve_did () | ||
180 | { | ||
181 | |||
182 | if (did == NULL) | ||
183 | { | ||
184 | printf ("Set DID option to resolve DID\n"); | ||
185 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
186 | ret = 1; | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | if (GNUNET_OK != DID_resolve (did, gns_handle, print_did_document, NULL)) | ||
191 | { | ||
192 | printf ("An error occured while resoling the DID\n"); | ||
193 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
194 | ret = 0; | ||
195 | return; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | |||
200 | /** | ||
201 | * @brief Signature of a callback function that is called after a did has been removed | ||
202 | */ | ||
203 | typedef void | ||
204 | (*remove_did_document_callback) (void *cls); | ||
205 | |||
206 | /** | ||
207 | * @brief A Structure containing a cont and cls. Can be passed as a cls to a callback function | ||
208 | * | ||
209 | */ | ||
210 | struct Event | ||
211 | { | ||
212 | remove_did_document_callback cont; | ||
213 | void *cls; | ||
214 | }; | ||
215 | |||
216 | /** | ||
217 | * @brief Implements the GNUNET_NAMESTORE_ContinuationWithStatus | ||
218 | * Calls the callback function and cls in the event struct | ||
219 | * | ||
220 | * @param cls closure containing the event struct | ||
221 | * @param success | ||
222 | * @param emgs | ||
223 | */ | ||
224 | static void | ||
225 | remove_did_document_namestore_cb (void *cls, enum GNUNET_ErrorCode ec) | ||
226 | { | ||
227 | struct Event *event; | ||
228 | |||
229 | if (GNUNET_EC_NONE == ec) | ||
230 | { | ||
231 | event = (struct Event *) cls; | ||
232 | |||
233 | if (event->cont != NULL) | ||
234 | { | ||
235 | event->cont (event->cls); | ||
236 | free (event); | ||
237 | } | ||
238 | else | ||
239 | { | ||
240 | free (event); | ||
241 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
242 | ret = 0; | ||
243 | return; | ||
244 | } | ||
245 | } | ||
246 | else | ||
247 | { | ||
248 | printf ("Something went wrong when deleting the DID Document\n"); | ||
249 | |||
250 | printf ("%s\n", GNUNET_ErrorCode_get_hint (ec)); | ||
251 | |||
252 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
253 | ret = 0; | ||
254 | return; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * @brief Callback called after the ego has been locked up | ||
261 | * | ||
262 | * @param cls closure | ||
263 | * @param ego the ego returned by the identity service | ||
264 | */ | ||
265 | static void | ||
266 | remove_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
267 | { | ||
268 | const struct GNUNET_CRYPTO_PrivateKey *skey = | ||
269 | GNUNET_IDENTITY_ego_get_private_key (ego); | ||
270 | |||
271 | GNUNET_NAMESTORE_record_set_store (namestore_handle, | ||
272 | skey, | ||
273 | GNUNET_GNS_EMPTY_LABEL_AT, | ||
274 | 0, | ||
275 | NULL, | ||
276 | &remove_did_document_namestore_cb, | ||
277 | cls); | ||
278 | } | ||
279 | |||
280 | |||
281 | /** | ||
282 | * @brief Remove a DID Document | ||
283 | */ | ||
284 | static void | ||
285 | remove_did_document (remove_did_document_callback cont, void *cls) | ||
286 | { | ||
287 | struct Event *event; | ||
288 | |||
289 | if (egoname == NULL) | ||
290 | { | ||
291 | printf ("Remove requieres an ego option\n"); | ||
292 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
293 | ret = 1; | ||
294 | return; | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | event = malloc (sizeof(*event)); | ||
299 | event->cont = cont; | ||
300 | event->cls = cls; | ||
301 | |||
302 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
303 | egoname, | ||
304 | &remove_did_document_ego_lookup_cb, | ||
305 | (void *) event); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | |||
310 | // Needed because create_did_ego_lookup_cb() and | ||
311 | // create_did_ego_create_cb() can call each other | ||
312 | static void create_did_ego_lockup_cb (); | ||
313 | |||
314 | /** | ||
315 | * @brief Create a DID(-Document). Called after DID has been created | ||
316 | * Prints status and the DID. | ||
317 | * | ||
318 | */ | ||
319 | static void | ||
320 | create_did_cb (enum GNUNET_GenericReturnValue status, void *cls) | ||
321 | { | ||
322 | if (GNUNET_OK == status) | ||
323 | { | ||
324 | printf ("DID has been created.\n%s\n", (char *) cls); | ||
325 | free (cls); | ||
326 | ret = 0; | ||
327 | } | ||
328 | else | ||
329 | { | ||
330 | printf ("An error occured while creating the DID.\n"); | ||
331 | ret = 1; | ||
332 | } | ||
333 | |||
334 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | |||
339 | /** | ||
340 | * @brief Create a DID(-Document) - Called after a new Identity has been created. | ||
341 | */ | ||
342 | static void | ||
343 | create_did_ego_create_cb (void *cls, | ||
344 | const struct GNUNET_CRYPTO_PrivateKey *pk, | ||
345 | enum GNUNET_ErrorCode ec) | ||
346 | { | ||
347 | if (GNUNET_EC_NONE != ec) | ||
348 | { | ||
349 | printf ("%s\n", GNUNET_ErrorCode_get_hint (ec)); | ||
350 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
351 | ret = 1; | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
356 | egoname, | ||
357 | &create_did_ego_lockup_cb, | ||
358 | NULL); | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * @brief Create a DID(-Document). Called after ego lookup | ||
364 | * | ||
365 | */ | ||
366 | static void | ||
367 | create_did_ego_lockup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
368 | { | ||
369 | if (ego == NULL) | ||
370 | { | ||
371 | // If Ego was not found. Create new one first | ||
372 | printf ("Ego was not found. Creating new one.\n"); | ||
373 | GNUNET_IDENTITY_create (identity_handle, | ||
374 | egoname, | ||
375 | NULL, | ||
376 | GNUNET_PUBLIC_KEY_TYPE_EDDSA, | ||
377 | &create_did_ego_create_cb, | ||
378 | egoname); | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | char *did = DID_identity_to_did (ego); | ||
383 | void *cls = malloc (strlen (did) + 1); | ||
384 | struct GNUNET_TIME_Relative expire_relative; | ||
385 | |||
386 | if (expire == NULL) | ||
387 | { | ||
388 | GNUNET_STRINGS_fancy_time_to_relative ( | ||
389 | DID_DOCUMENT_DEFAULT_EXPIRATION_TIME, &expire_relative); | ||
390 | } | ||
391 | else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (expire, | ||
392 | & | ||
393 | expire_relative)) | ||
394 | { | ||
395 | printf ("Failed to read given expiration time\n"); | ||
396 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
397 | ret = 1; | ||
398 | return; | ||
399 | } | ||
400 | |||
401 | strcpy (cls, did); | ||
402 | // TODO: Add DID_document argument | ||
403 | if (GNUNET_OK != DID_create (ego, | ||
404 | NULL, | ||
405 | &expire_relative, | ||
406 | namestore_handle, | ||
407 | create_did_cb, | ||
408 | cls)) | ||
409 | { | ||
410 | printf ("An error occured while creating the DID.\n"); | ||
411 | ret = 1; | ||
412 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
413 | return; | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | |||
419 | /** | ||
420 | * @brief Create a DID(-Document). | ||
421 | * | ||
422 | */ | ||
423 | static void | ||
424 | create_did () | ||
425 | { | ||
426 | // Ego name to be set | ||
427 | if (egoname == NULL) | ||
428 | { | ||
429 | printf ("Set the Ego argument to create a new DID(-Document)\n"); | ||
430 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
431 | ret = 1; | ||
432 | return; | ||
433 | } | ||
434 | |||
435 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
436 | egoname, | ||
437 | &create_did_ego_lockup_cb, | ||
438 | NULL); | ||
439 | } | ||
440 | |||
441 | |||
442 | /** | ||
443 | * @brief Replace a DID Docuemnt. Callback function after ego lockup | ||
444 | * | ||
445 | * @param cls | ||
446 | * @param ego | ||
447 | */ | ||
448 | static void | ||
449 | replace_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
450 | { | ||
451 | // create_did_store (didd, ego); | ||
452 | } | ||
453 | |||
454 | |||
455 | /** | ||
456 | * @brief Replace a DID Document. Callback functiona after remove | ||
457 | * | ||
458 | * @param cls | ||
459 | */ | ||
460 | static void | ||
461 | replace_did_document_remove_cb (void *cls) | ||
462 | { | ||
463 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
464 | egoname, | ||
465 | &replace_did_document_ego_lookup_cb, | ||
466 | NULL); | ||
467 | } | ||
468 | |||
469 | |||
470 | /** | ||
471 | * @brief Replace a DID Docuemnt | ||
472 | * | ||
473 | */ | ||
474 | static void | ||
475 | replace_did_document () | ||
476 | { | ||
477 | if ((didd != NULL) && (expire != NULL)) | ||
478 | { | ||
479 | remove_did_document (&replace_did_document_remove_cb, NULL); | ||
480 | } | ||
481 | else | ||
482 | { | ||
483 | printf ( | ||
484 | "Set the DID Document and expiration time argument to replace the DID Document\n"); | ||
485 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
486 | ret = 1; | ||
487 | return; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | |||
492 | static void | ||
493 | post_ego_iteration (void *cls) | ||
494 | { | ||
495 | // TODO: Check that only one argument is set | ||
496 | |||
497 | if (1 == replace) | ||
498 | { | ||
499 | replace_did_document (); | ||
500 | } | ||
501 | else if (1 == get) | ||
502 | { | ||
503 | resolve_did (); | ||
504 | } | ||
505 | else if (1 == remove_did) | ||
506 | { | ||
507 | remove_did_document (NULL, NULL); | ||
508 | } | ||
509 | else if (1 == create) | ||
510 | { | ||
511 | create_did (); | ||
512 | } | ||
513 | else | ||
514 | { | ||
515 | // No Argument found | ||
516 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
517 | return; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | |||
522 | static void | ||
523 | process_dids (void *cls, struct GNUNET_IDENTITY_Ego *ego, | ||
524 | void **ctx, const char*name) | ||
525 | { | ||
526 | char *did_str; | ||
527 | |||
528 | if (ego == NULL) | ||
529 | { | ||
530 | if (1 == ego_exists) | ||
531 | { | ||
532 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
533 | return; | ||
534 | } | ||
535 | GNUNET_SCHEDULER_add_now (&post_ego_iteration, NULL); | ||
536 | return; | ||
537 | } | ||
538 | |||
539 | if (1 == show_all) | ||
540 | { | ||
541 | did_str = DID_identity_to_did (ego); | ||
542 | printf ("%s:\n\t%s\n", name, did_str); | ||
543 | GNUNET_free (did_str); | ||
544 | return; | ||
545 | } | ||
546 | if (1 == show) | ||
547 | { | ||
548 | if (0 == strncmp (name, egoname, strlen (egoname))) | ||
549 | { | ||
550 | did_str = DID_identity_to_did (ego); | ||
551 | printf ("%s:\n\t%s\n", name, did_str); | ||
552 | GNUNET_free (did_str); | ||
553 | return; | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | |||
558 | |||
559 | static void | ||
560 | run (void *cls, | ||
561 | char *const *args, | ||
562 | const char *cfgfile, | ||
563 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
564 | { | ||
565 | gns_handle = GNUNET_GNS_connect (c); | ||
566 | namestore_handle = GNUNET_NAMESTORE_connect (c); | ||
567 | my_cfg = c; | ||
568 | |||
569 | // check if GNS_handle could connect | ||
570 | if (gns_handle == NULL) | ||
571 | { | ||
572 | ret = 1; | ||
573 | return; | ||
574 | } | ||
575 | |||
576 | // check if NAMESTORE_handle could connect | ||
577 | if (namestore_handle == NULL) | ||
578 | { | ||
579 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
580 | ret = 1; | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | identity_handle = GNUNET_IDENTITY_connect (c, &process_dids, NULL); | ||
585 | if (identity_handle == NULL) | ||
586 | { | ||
587 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
588 | ret = 1; | ||
589 | return; | ||
590 | } | ||
591 | } | ||
592 | |||
593 | |||
594 | int | ||
595 | main (int argc, char *const argv[]) | ||
596 | { | ||
597 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
598 | GNUNET_GETOPT_option_flag ('C', | ||
599 | "create", | ||
600 | gettext_noop ( | ||
601 | "Create a DID Document and display its DID"), | ||
602 | &create), | ||
603 | GNUNET_GETOPT_option_flag ('g', | ||
604 | "get", | ||
605 | gettext_noop ( | ||
606 | "Get the DID Document associated with the given DID"), | ||
607 | &get), | ||
608 | GNUNET_GETOPT_option_flag ('r', | ||
609 | "remove", | ||
610 | gettext_noop ( | ||
611 | "Remove the DID"), | ||
612 | &remove_did), | ||
613 | GNUNET_GETOPT_option_flag ('R', | ||
614 | "replace", | ||
615 | gettext_noop ("Replace the DID Document."), | ||
616 | &replace), | ||
617 | GNUNET_GETOPT_option_flag ('s', | ||
618 | "show", | ||
619 | gettext_noop ("Show the DID for a given ego"), | ||
620 | &show), | ||
621 | GNUNET_GETOPT_option_flag ('A', | ||
622 | "show-all", | ||
623 | gettext_noop ("Show egos with DIDs"), | ||
624 | &show_all), | ||
625 | GNUNET_GETOPT_option_string ('d', | ||
626 | "did", | ||
627 | "DID", | ||
628 | gettext_noop ( | ||
629 | "The Decentralized Identity (DID)"), | ||
630 | &did), | ||
631 | GNUNET_GETOPT_option_string ('D', | ||
632 | "did-document", | ||
633 | "JSON", | ||
634 | gettext_noop ( | ||
635 | "The DID Document to store in GNUNET"), | ||
636 | &didd), | ||
637 | GNUNET_GETOPT_option_string ('e', | ||
638 | "ego", | ||
639 | "EGO", | ||
640 | gettext_noop ("The name of the EGO"), | ||
641 | &egoname), | ||
642 | GNUNET_GETOPT_option_string ('t', | ||
643 | "expiration-time", | ||
644 | "TIME", | ||
645 | gettext_noop ( | ||
646 | "The time until the DID Document is going to expire (e.g. 5d)"), | ||
647 | &expire), | ||
648 | GNUNET_GETOPT_OPTION_END | ||
649 | }; | ||
650 | |||
651 | if (GNUNET_OK != GNUNET_PROGRAM_run (argc, | ||
652 | argv, | ||
653 | "gnunet-did", | ||
654 | "Manage Decentralized Identities (DIDs)", | ||
655 | options, | ||
656 | &run, | ||
657 | NULL)) | ||
658 | return 1; | ||
659 | else | ||
660 | return ret; | ||
661 | } | ||
diff --git a/src/cli/reclaim/gnunet-reclaim.c b/src/cli/reclaim/gnunet-reclaim.c new file mode 100644 index 000000000..94bceb8da --- /dev/null +++ b/src/cli/reclaim/gnunet-reclaim.c | |||
@@ -0,0 +1,943 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2015 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 | * @author Martin Schanzenbach | ||
22 | * @file src/reclaim/gnunet-reclaim.c | ||
23 | * @brief Identity Provider utility | ||
24 | * | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include <inttypes.h> | ||
28 | |||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | #include "gnunet_identity_service.h" | ||
32 | #include "gnunet_namestore_service.h" | ||
33 | #include "gnunet_reclaim_service.h" | ||
34 | #include "gnunet_signatures.h" | ||
35 | /** | ||
36 | * return value | ||
37 | */ | ||
38 | static int ret; | ||
39 | |||
40 | /** | ||
41 | * List attribute flag | ||
42 | */ | ||
43 | static int list; | ||
44 | |||
45 | /** | ||
46 | * List credentials flag | ||
47 | */ | ||
48 | static int list_credentials; | ||
49 | |||
50 | /** | ||
51 | * Credential ID string | ||
52 | */ | ||
53 | static char *credential_id; | ||
54 | |||
55 | /** | ||
56 | * The expected RP URI | ||
57 | */ | ||
58 | static char *ex_rp_uri; | ||
59 | |||
60 | /** | ||
61 | * Credential ID | ||
62 | */ | ||
63 | static struct GNUNET_RECLAIM_Identifier credential; | ||
64 | |||
65 | /** | ||
66 | * Credential name | ||
67 | */ | ||
68 | static char *credential_name; | ||
69 | |||
70 | /** | ||
71 | * Credential type | ||
72 | */ | ||
73 | static char *credential_type; | ||
74 | |||
75 | /** | ||
76 | * Credential exists | ||
77 | */ | ||
78 | static int credential_exists; | ||
79 | |||
80 | /** | ||
81 | * Relying party | ||
82 | */ | ||
83 | static char *rp; | ||
84 | |||
85 | /** | ||
86 | * The attribute | ||
87 | */ | ||
88 | static char *attr_name; | ||
89 | |||
90 | /** | ||
91 | * Attribute value | ||
92 | */ | ||
93 | static char *attr_value; | ||
94 | |||
95 | /** | ||
96 | * Attributes to issue | ||
97 | */ | ||
98 | static char *issue_attrs; | ||
99 | |||
100 | /** | ||
101 | * Ticket to consume | ||
102 | */ | ||
103 | static char *consume_ticket; | ||
104 | |||
105 | /** | ||
106 | * Attribute type | ||
107 | */ | ||
108 | static char *type_str; | ||
109 | |||
110 | /** | ||
111 | * Ticket to revoke | ||
112 | */ | ||
113 | static char *revoke_ticket; | ||
114 | |||
115 | /** | ||
116 | * Ticket listing | ||
117 | */ | ||
118 | static int list_tickets; | ||
119 | |||
120 | /** | ||
121 | * Ego name | ||
122 | */ | ||
123 | static char *ego_name; | ||
124 | |||
125 | /** | ||
126 | * Identity handle | ||
127 | */ | ||
128 | static struct GNUNET_IDENTITY_Handle *identity_handle; | ||
129 | |||
130 | /** | ||
131 | * reclaim handle | ||
132 | */ | ||
133 | static struct GNUNET_RECLAIM_Handle *reclaim_handle; | ||
134 | |||
135 | /** | ||
136 | * reclaim operation | ||
137 | */ | ||
138 | static struct GNUNET_RECLAIM_Operation *reclaim_op; | ||
139 | |||
140 | /** | ||
141 | * Attribute iterator | ||
142 | */ | ||
143 | static struct GNUNET_RECLAIM_AttributeIterator *attr_iterator; | ||
144 | |||
145 | /** | ||
146 | * Credential iterator | ||
147 | */ | ||
148 | static struct GNUNET_RECLAIM_CredentialIterator *cred_iterator; | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Ticket iterator | ||
153 | */ | ||
154 | static struct GNUNET_RECLAIM_TicketIterator *ticket_iterator; | ||
155 | |||
156 | |||
157 | /** | ||
158 | * ego private key | ||
159 | */ | ||
160 | static const struct GNUNET_CRYPTO_PrivateKey *pkey; | ||
161 | |||
162 | /** | ||
163 | * Ticket to consume | ||
164 | */ | ||
165 | static struct GNUNET_RECLAIM_Ticket ticket; | ||
166 | |||
167 | /** | ||
168 | * Attribute list | ||
169 | */ | ||
170 | static struct GNUNET_RECLAIM_AttributeList *attr_list; | ||
171 | |||
172 | /** | ||
173 | * Attribute expiration interval | ||
174 | */ | ||
175 | static struct GNUNET_TIME_Relative exp_interval; | ||
176 | |||
177 | /** | ||
178 | * Timeout task | ||
179 | */ | ||
180 | static struct GNUNET_SCHEDULER_Task *timeout; | ||
181 | |||
182 | /** | ||
183 | * Cleanup task | ||
184 | */ | ||
185 | static struct GNUNET_SCHEDULER_Task *cleanup_task; | ||
186 | |||
187 | /** | ||
188 | * Claim to store | ||
189 | */ | ||
190 | struct GNUNET_RECLAIM_Attribute *claim; | ||
191 | |||
192 | /** | ||
193 | * Claim to delete | ||
194 | */ | ||
195 | static char *attr_delete; | ||
196 | |||
197 | /** | ||
198 | * Claim object to delete | ||
199 | */ | ||
200 | static struct GNUNET_RECLAIM_Attribute *attr_to_delete; | ||
201 | |||
202 | static void | ||
203 | do_cleanup (void *cls) | ||
204 | { | ||
205 | cleanup_task = NULL; | ||
206 | if (NULL != timeout) | ||
207 | GNUNET_SCHEDULER_cancel (timeout); | ||
208 | if (NULL != reclaim_op) | ||
209 | GNUNET_RECLAIM_cancel (reclaim_op); | ||
210 | if (NULL != attr_iterator) | ||
211 | GNUNET_RECLAIM_get_attributes_stop (attr_iterator); | ||
212 | if (NULL != cred_iterator) | ||
213 | GNUNET_RECLAIM_get_credentials_stop (cred_iterator); | ||
214 | if (NULL != ticket_iterator) | ||
215 | GNUNET_RECLAIM_ticket_iteration_stop (ticket_iterator); | ||
216 | if (NULL != reclaim_handle) | ||
217 | GNUNET_RECLAIM_disconnect (reclaim_handle); | ||
218 | if (NULL != identity_handle) | ||
219 | GNUNET_IDENTITY_disconnect (identity_handle); | ||
220 | if (NULL != attr_list) | ||
221 | { | ||
222 | GNUNET_RECLAIM_attribute_list_destroy (attr_list); | ||
223 | attr_list = NULL; | ||
224 | } | ||
225 | if (NULL != attr_to_delete) | ||
226 | GNUNET_free (attr_to_delete); | ||
227 | if (NULL == credential_type) | ||
228 | GNUNET_free (credential_type); | ||
229 | } | ||
230 | |||
231 | |||
232 | static void | ||
233 | ticket_issue_cb (void *cls, | ||
234 | const struct GNUNET_RECLAIM_Ticket *ticket, | ||
235 | const struct GNUNET_RECLAIM_PresentationList *presentations) | ||
236 | { | ||
237 | reclaim_op = NULL; | ||
238 | if (NULL != ticket) | ||
239 | { | ||
240 | printf ("%s\n", ticket->gns_name); | ||
241 | } | ||
242 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
243 | } | ||
244 | |||
245 | |||
246 | static void | ||
247 | store_cont (void *cls, int32_t success, const char *emsg) | ||
248 | { | ||
249 | reclaim_op = NULL; | ||
250 | if (GNUNET_SYSERR == success) | ||
251 | { | ||
252 | fprintf (stderr, "%s\n", emsg); | ||
253 | } | ||
254 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
255 | } | ||
256 | |||
257 | |||
258 | static void | ||
259 | process_attrs (void *cls, | ||
260 | const struct GNUNET_CRYPTO_PublicKey *identity, | ||
261 | const struct GNUNET_RECLAIM_Attribute *attr, | ||
262 | const struct GNUNET_RECLAIM_Presentation *presentation) | ||
263 | { | ||
264 | char *value_str; | ||
265 | char *id; | ||
266 | const char *attr_type; | ||
267 | |||
268 | if (NULL == identity) | ||
269 | { | ||
270 | reclaim_op = NULL; | ||
271 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
272 | return; | ||
273 | } | ||
274 | if (NULL == attr) | ||
275 | { | ||
276 | ret = 1; | ||
277 | return; | ||
278 | } | ||
279 | attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type); | ||
280 | id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); | ||
281 | value_str = NULL; | ||
282 | if (NULL == presentation) | ||
283 | { | ||
284 | value_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type, | ||
285 | attr->data, | ||
286 | attr->data_size); | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | struct GNUNET_RECLAIM_AttributeListEntry *ale; | ||
291 | struct GNUNET_RECLAIM_AttributeList *al | ||
292 | = GNUNET_RECLAIM_presentation_get_attributes (presentation); | ||
293 | |||
294 | for (ale = al->list_head; NULL != ale; ale = ale->next) | ||
295 | { | ||
296 | if (0 != strncmp (attr->data, ale->attribute->name, attr->data_size)) | ||
297 | continue; | ||
298 | value_str | ||
299 | = GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type, | ||
300 | ale->attribute->data, | ||
301 | ale->attribute->data_size); | ||
302 | break; | ||
303 | } | ||
304 | } | ||
305 | fprintf (stdout, | ||
306 | "Name: %s; Value: %s (%s); Flag %u; ID: %s %s\n", | ||
307 | attr->name, | ||
308 | (NULL != value_str) ? value_str : "???", | ||
309 | attr_type, | ||
310 | attr->flag, | ||
311 | id, | ||
312 | (NULL == presentation) ? "" : "(ATTESTED)"); | ||
313 | GNUNET_free (value_str); | ||
314 | GNUNET_free (id); | ||
315 | } | ||
316 | |||
317 | |||
318 | static void | ||
319 | ticket_iter_err (void *cls) | ||
320 | { | ||
321 | ticket_iterator = NULL; | ||
322 | fprintf (stderr, "Failed to iterate over tickets\n"); | ||
323 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
324 | } | ||
325 | |||
326 | |||
327 | static void | ||
328 | ticket_iter_fin (void *cls) | ||
329 | { | ||
330 | ticket_iterator = NULL; | ||
331 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
332 | } | ||
333 | |||
334 | |||
335 | static void | ||
336 | ticket_iter (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket, const char* rp_uri) | ||
337 | { | ||
338 | fprintf (stdout, "Ticket: %s | RP URI: %s\n", ticket->gns_name, rp_uri); | ||
339 | GNUNET_RECLAIM_ticket_iteration_next (ticket_iterator); | ||
340 | } | ||
341 | |||
342 | |||
343 | static void | ||
344 | iter_error (void *cls) | ||
345 | { | ||
346 | attr_iterator = NULL; | ||
347 | cred_iterator = NULL; | ||
348 | fprintf (stderr, "Failed\n"); | ||
349 | |||
350 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
351 | } | ||
352 | |||
353 | |||
354 | static void | ||
355 | timeout_task (void *cls) | ||
356 | { | ||
357 | timeout = NULL; | ||
358 | ret = 1; | ||
359 | fprintf (stderr, "Timeout\n"); | ||
360 | if (NULL == cleanup_task) | ||
361 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
362 | } | ||
363 | |||
364 | |||
365 | static void | ||
366 | process_rvk (void *cls, int success, const char *msg) | ||
367 | { | ||
368 | reclaim_op = NULL; | ||
369 | if (GNUNET_OK != success) | ||
370 | { | ||
371 | fprintf (stderr, "Revocation failed.\n"); | ||
372 | ret = 1; | ||
373 | } | ||
374 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
375 | } | ||
376 | |||
377 | |||
378 | static void | ||
379 | process_delete (void *cls, int success, const char *msg) | ||
380 | { | ||
381 | reclaim_op = NULL; | ||
382 | if (GNUNET_OK != success) | ||
383 | { | ||
384 | fprintf (stderr, "Deletion failed.\n"); | ||
385 | ret = 1; | ||
386 | } | ||
387 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
388 | } | ||
389 | |||
390 | |||
391 | static void | ||
392 | iter_finished (void *cls) | ||
393 | { | ||
394 | struct GNUNET_RECLAIM_AttributeListEntry *le; | ||
395 | char *attrs_tmp; | ||
396 | char *attr_str; | ||
397 | char *data; | ||
398 | size_t data_size; | ||
399 | int type; | ||
400 | |||
401 | attr_iterator = NULL; | ||
402 | if (list) | ||
403 | { | ||
404 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
405 | return; | ||
406 | } | ||
407 | |||
408 | if (issue_attrs) | ||
409 | { | ||
410 | attrs_tmp = GNUNET_strdup (issue_attrs); | ||
411 | attr_str = strtok (attrs_tmp, ","); | ||
412 | while (NULL != attr_str) | ||
413 | { | ||
414 | le = attr_list->list_head; | ||
415 | while (le) | ||
416 | { | ||
417 | if (0 == strcasecmp (attr_str, le->attribute->name)) | ||
418 | break; | ||
419 | |||
420 | le = le->next; | ||
421 | } | ||
422 | |||
423 | if (! le) | ||
424 | { | ||
425 | fprintf (stdout, "No such attribute ``%s''\n", attr_str); | ||
426 | break; | ||
427 | } | ||
428 | attr_str = strtok (NULL, ","); | ||
429 | } | ||
430 | GNUNET_free (attrs_tmp); | ||
431 | if (NULL != attr_str) | ||
432 | { | ||
433 | GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
434 | return; | ||
435 | } | ||
436 | if (NULL == ex_rp_uri) | ||
437 | { | ||
438 | fprintf (stdout, "No RP URI provided\n"); | ||
439 | GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
440 | return; | ||
441 | } | ||
442 | reclaim_op = GNUNET_RECLAIM_ticket_issue (reclaim_handle, | ||
443 | pkey, | ||
444 | ex_rp_uri, | ||
445 | attr_list, | ||
446 | &ticket_issue_cb, | ||
447 | NULL); | ||
448 | return; | ||
449 | } | ||
450 | if (consume_ticket) | ||
451 | { | ||
452 | if (NULL == ex_rp_uri) | ||
453 | { | ||
454 | fprintf (stderr, "Expected an RP URI to consume ticket\n"); | ||
455 | GNUNET_SCHEDULER_add_now(&do_cleanup, NULL); | ||
456 | return; | ||
457 | } | ||
458 | reclaim_op = GNUNET_RECLAIM_ticket_consume (reclaim_handle, | ||
459 | &ticket, | ||
460 | ex_rp_uri, | ||
461 | &process_attrs, | ||
462 | NULL); | ||
463 | timeout = GNUNET_SCHEDULER_add_delayed ( | ||
464 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), | ||
465 | &timeout_task, | ||
466 | NULL); | ||
467 | return; | ||
468 | } | ||
469 | if (revoke_ticket) | ||
470 | { | ||
471 | reclaim_op = GNUNET_RECLAIM_ticket_revoke (reclaim_handle, | ||
472 | pkey, | ||
473 | &ticket, | ||
474 | &process_rvk, | ||
475 | NULL); | ||
476 | return; | ||
477 | } | ||
478 | if (attr_delete) | ||
479 | { | ||
480 | if (NULL == attr_to_delete) | ||
481 | { | ||
482 | fprintf (stdout, "No such attribute ``%s''\n", attr_delete); | ||
483 | GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
484 | return; | ||
485 | } | ||
486 | reclaim_op = GNUNET_RECLAIM_attribute_delete (reclaim_handle, | ||
487 | pkey, | ||
488 | attr_to_delete, | ||
489 | &process_delete, | ||
490 | NULL); | ||
491 | return; | ||
492 | } | ||
493 | if (attr_name) | ||
494 | { | ||
495 | if (NULL == type_str) | ||
496 | type = GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING; | ||
497 | else | ||
498 | type = GNUNET_RECLAIM_attribute_typename_to_number (type_str); | ||
499 | |||
500 | GNUNET_assert (GNUNET_SYSERR != | ||
501 | GNUNET_RECLAIM_attribute_string_to_value (type, | ||
502 | attr_value, | ||
503 | (void **) &data, | ||
504 | &data_size)); | ||
505 | if (NULL != claim) | ||
506 | { | ||
507 | claim->type = type; | ||
508 | claim->data = data; | ||
509 | claim->data_size = data_size; | ||
510 | } | ||
511 | else | ||
512 | { | ||
513 | claim = | ||
514 | GNUNET_RECLAIM_attribute_new (attr_name, NULL, type, data, data_size); | ||
515 | } | ||
516 | if (NULL != credential_id) | ||
517 | { | ||
518 | claim->credential = credential; | ||
519 | } | ||
520 | reclaim_op = GNUNET_RECLAIM_attribute_store (reclaim_handle, | ||
521 | pkey, | ||
522 | claim, | ||
523 | &exp_interval, | ||
524 | &store_cont, | ||
525 | NULL); | ||
526 | GNUNET_free (data); | ||
527 | GNUNET_free (claim); | ||
528 | return; | ||
529 | } | ||
530 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
531 | } | ||
532 | |||
533 | |||
534 | static void | ||
535 | iter_cb (void *cls, | ||
536 | const struct GNUNET_CRYPTO_PublicKey *identity, | ||
537 | const struct GNUNET_RECLAIM_Attribute *attr) | ||
538 | { | ||
539 | struct GNUNET_RECLAIM_AttributeListEntry *le; | ||
540 | char *attrs_tmp; | ||
541 | char *attr_str; | ||
542 | char *label; | ||
543 | char *id; | ||
544 | const char *attr_type; | ||
545 | |||
546 | if ((NULL != attr_name) && (NULL == claim)) | ||
547 | { | ||
548 | if (0 == strcasecmp (attr_name, attr->name)) | ||
549 | { | ||
550 | claim = GNUNET_RECLAIM_attribute_new (attr->name, | ||
551 | &attr->credential, | ||
552 | attr->type, | ||
553 | attr->data, | ||
554 | attr->data_size); | ||
555 | claim->id = attr->id; | ||
556 | } | ||
557 | } | ||
558 | else if (issue_attrs) | ||
559 | { | ||
560 | attrs_tmp = GNUNET_strdup (issue_attrs); | ||
561 | attr_str = strtok (attrs_tmp, ","); | ||
562 | while (NULL != attr_str) | ||
563 | { | ||
564 | if (0 != strcasecmp (attr_str, attr->name)) | ||
565 | { | ||
566 | attr_str = strtok (NULL, ","); | ||
567 | continue; | ||
568 | } | ||
569 | le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); | ||
570 | le->attribute = GNUNET_RECLAIM_attribute_new (attr->name, | ||
571 | &attr->credential, | ||
572 | attr->type, | ||
573 | attr->data, | ||
574 | attr->data_size); | ||
575 | le->attribute->flag = attr->flag; | ||
576 | le->attribute->id = attr->id; | ||
577 | GNUNET_CONTAINER_DLL_insert (attr_list->list_head, | ||
578 | attr_list->list_tail, | ||
579 | le); | ||
580 | break; | ||
581 | } | ||
582 | GNUNET_free (attrs_tmp); | ||
583 | } | ||
584 | else if (attr_delete && (NULL == attr_to_delete)) | ||
585 | { | ||
586 | label = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); | ||
587 | if (0 == strcasecmp (attr_delete, label)) | ||
588 | { | ||
589 | attr_to_delete = GNUNET_RECLAIM_attribute_new (attr->name, | ||
590 | &attr->credential, | ||
591 | attr->type, | ||
592 | attr->data, | ||
593 | attr->data_size); | ||
594 | attr_to_delete->id = attr->id; | ||
595 | } | ||
596 | GNUNET_free (label); | ||
597 | } | ||
598 | else if (list) | ||
599 | { | ||
600 | attr_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type, | ||
601 | attr->data, | ||
602 | attr->data_size); | ||
603 | attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type); | ||
604 | id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); | ||
605 | if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->credential)) | ||
606 | { | ||
607 | fprintf (stdout, | ||
608 | "%s: ``%s'' (%s); ID: %s\n", | ||
609 | attr->name, | ||
610 | attr_str, | ||
611 | attr_type, | ||
612 | id); | ||
613 | } | ||
614 | else | ||
615 | { | ||
616 | char *cred_id = | ||
617 | GNUNET_STRINGS_data_to_string_alloc (&attr->credential, | ||
618 | sizeof(attr->credential)); | ||
619 | fprintf (stdout, | ||
620 | "%s: ``%s'' in credential presentation `%s' (%s); ID: %s\n", | ||
621 | attr->name, | ||
622 | attr_str, | ||
623 | cred_id, | ||
624 | attr_type, | ||
625 | id); | ||
626 | GNUNET_free (cred_id); | ||
627 | |||
628 | } | ||
629 | GNUNET_free (id); | ||
630 | } | ||
631 | GNUNET_RECLAIM_get_attributes_next (attr_iterator); | ||
632 | } | ||
633 | |||
634 | |||
635 | static void | ||
636 | cred_iter_finished (void *cls) | ||
637 | { | ||
638 | cred_iterator = NULL; | ||
639 | |||
640 | // Add new credential | ||
641 | if ((NULL != credential_name) && | ||
642 | (NULL != attr_value)) | ||
643 | { | ||
644 | enum GNUNET_RECLAIM_CredentialType ctype = | ||
645 | GNUNET_RECLAIM_credential_typename_to_number (credential_type); | ||
646 | struct GNUNET_RECLAIM_Credential *credential = | ||
647 | GNUNET_RECLAIM_credential_new (credential_name, | ||
648 | ctype, | ||
649 | attr_value, | ||
650 | strlen (attr_value)); | ||
651 | reclaim_op = GNUNET_RECLAIM_credential_store (reclaim_handle, | ||
652 | pkey, | ||
653 | credential, | ||
654 | &exp_interval, | ||
655 | store_cont, | ||
656 | NULL); | ||
657 | return; | ||
658 | |||
659 | } | ||
660 | if (list_credentials) | ||
661 | { | ||
662 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
663 | return; | ||
664 | } | ||
665 | attr_iterator = GNUNET_RECLAIM_get_attributes_start (reclaim_handle, | ||
666 | pkey, | ||
667 | &iter_error, | ||
668 | NULL, | ||
669 | &iter_cb, | ||
670 | NULL, | ||
671 | &iter_finished, | ||
672 | NULL); | ||
673 | |||
674 | } | ||
675 | |||
676 | |||
677 | static void | ||
678 | cred_iter_cb (void *cls, | ||
679 | const struct GNUNET_CRYPTO_PublicKey *identity, | ||
680 | const struct GNUNET_RECLAIM_Credential *cred) | ||
681 | { | ||
682 | char *cred_str; | ||
683 | char *attr_str; | ||
684 | char *id; | ||
685 | const char *cred_type; | ||
686 | struct GNUNET_RECLAIM_AttributeListEntry *ale; | ||
687 | |||
688 | if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&credential, | ||
689 | &cred->id)) | ||
690 | credential_exists = GNUNET_YES; | ||
691 | if (list_credentials) | ||
692 | { | ||
693 | cred_str = GNUNET_RECLAIM_credential_value_to_string (cred->type, | ||
694 | cred->data, | ||
695 | cred->data_size); | ||
696 | cred_type = GNUNET_RECLAIM_credential_number_to_typename (cred->type); | ||
697 | id = GNUNET_STRINGS_data_to_string_alloc (&cred->id, sizeof(cred->id)); | ||
698 | fprintf (stdout, | ||
699 | "%s: ``%s'' (%s); ID: %s\n", | ||
700 | cred->name, | ||
701 | cred_str, | ||
702 | cred_type, | ||
703 | id); | ||
704 | struct GNUNET_RECLAIM_AttributeList *attrs = | ||
705 | GNUNET_RECLAIM_credential_get_attributes (cred); | ||
706 | if (NULL != attrs) | ||
707 | { | ||
708 | fprintf (stdout, | ||
709 | "\t Attributes:\n"); | ||
710 | for (ale = attrs->list_head; NULL != ale; ale = ale->next) | ||
711 | { | ||
712 | attr_str = GNUNET_RECLAIM_attribute_value_to_string ( | ||
713 | ale->attribute->type, | ||
714 | ale->attribute->data, | ||
715 | ale->attribute->data_size); | ||
716 | fprintf (stdout, | ||
717 | "\t %s: %s\n", ale->attribute->name, attr_str); | ||
718 | GNUNET_free (attr_str); | ||
719 | } | ||
720 | GNUNET_RECLAIM_attribute_list_destroy (attrs); | ||
721 | } | ||
722 | GNUNET_free (id); | ||
723 | } | ||
724 | GNUNET_RECLAIM_get_credentials_next (cred_iterator); | ||
725 | } | ||
726 | |||
727 | |||
728 | static void | ||
729 | start_process () | ||
730 | { | ||
731 | if (NULL == pkey) | ||
732 | { | ||
733 | fprintf (stderr, "Ego %s not found\n", ego_name); | ||
734 | cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); | ||
735 | return; | ||
736 | } | ||
737 | if (NULL == credential_type) | ||
738 | credential_type = GNUNET_strdup ("JWT"); | ||
739 | credential = GNUNET_RECLAIM_ID_ZERO; | ||
740 | if (NULL != credential_id) | ||
741 | GNUNET_STRINGS_string_to_data (credential_id, | ||
742 | strlen (credential_id), | ||
743 | &credential, sizeof(credential)); | ||
744 | credential_exists = GNUNET_NO; | ||
745 | if (list_tickets) | ||
746 | { | ||
747 | ticket_iterator = GNUNET_RECLAIM_ticket_iteration_start (reclaim_handle, | ||
748 | pkey, | ||
749 | &ticket_iter_err, | ||
750 | NULL, | ||
751 | &ticket_iter, | ||
752 | NULL, | ||
753 | &ticket_iter_fin, | ||
754 | NULL); | ||
755 | return; | ||
756 | } | ||
757 | |||
758 | if (NULL != consume_ticket) | ||
759 | memcpy (ticket.gns_name, consume_ticket, strlen (consume_ticket) + 1); | ||
760 | if (NULL != revoke_ticket) | ||
761 | GNUNET_STRINGS_string_to_data (revoke_ticket, | ||
762 | strlen (revoke_ticket), | ||
763 | &ticket, | ||
764 | sizeof(struct GNUNET_RECLAIM_Ticket)); | ||
765 | |||
766 | attr_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); | ||
767 | claim = NULL; | ||
768 | cred_iterator = GNUNET_RECLAIM_get_credentials_start (reclaim_handle, | ||
769 | pkey, | ||
770 | &iter_error, | ||
771 | NULL, | ||
772 | &cred_iter_cb, | ||
773 | NULL, | ||
774 | &cred_iter_finished, | ||
775 | NULL); | ||
776 | |||
777 | } | ||
778 | |||
779 | |||
780 | static int init = GNUNET_YES; | ||
781 | |||
782 | static void | ||
783 | ego_cb (void *cls, | ||
784 | struct GNUNET_IDENTITY_Ego *ego, | ||
785 | void **ctx, | ||
786 | const char *name) | ||
787 | { | ||
788 | if (NULL == name) | ||
789 | { | ||
790 | if (GNUNET_YES == init) | ||
791 | { | ||
792 | init = GNUNET_NO; | ||
793 | start_process (); | ||
794 | } | ||
795 | return; | ||
796 | } | ||
797 | if (0 != strcmp (name, ego_name)) | ||
798 | return; | ||
799 | pkey = GNUNET_IDENTITY_ego_get_private_key (ego); | ||
800 | } | ||
801 | |||
802 | |||
803 | static void | ||
804 | run (void *cls, | ||
805 | char *const *args, | ||
806 | const char *cfgfile, | ||
807 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
808 | { | ||
809 | ret = 0; | ||
810 | if (NULL == ego_name) | ||
811 | { | ||
812 | ret = 1; | ||
813 | fprintf (stderr, _ ("Ego is required\n")); | ||
814 | return; | ||
815 | } | ||
816 | |||
817 | if ((NULL == attr_value) && (NULL != attr_name)) | ||
818 | { | ||
819 | ret = 1; | ||
820 | fprintf (stderr, _ ("Attribute value missing!\n")); | ||
821 | return; | ||
822 | } | ||
823 | |||
824 | if ((NULL == rp) && (NULL != issue_attrs)) | ||
825 | { | ||
826 | ret = 1; | ||
827 | fprintf (stderr, _ ("Requesting party key is required!\n")); | ||
828 | return; | ||
829 | } | ||
830 | |||
831 | reclaim_handle = GNUNET_RECLAIM_connect (c); | ||
832 | // Get Ego | ||
833 | identity_handle = GNUNET_IDENTITY_connect (c, &ego_cb, NULL); | ||
834 | } | ||
835 | |||
836 | |||
837 | int | ||
838 | main (int argc, char *const argv[]) | ||
839 | { | ||
840 | exp_interval = GNUNET_TIME_UNIT_HOURS; | ||
841 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
842 | GNUNET_GETOPT_option_string ('a', | ||
843 | "add", | ||
844 | "NAME", | ||
845 | gettext_noop ( | ||
846 | "Add or update an attribute NAME"), | ||
847 | &attr_name), | ||
848 | GNUNET_GETOPT_option_string ('d', | ||
849 | "delete", | ||
850 | "ID", | ||
851 | gettext_noop ("Delete the attribute with ID"), | ||
852 | &attr_delete), | ||
853 | GNUNET_GETOPT_option_string ('V', | ||
854 | "value", | ||
855 | "VALUE", | ||
856 | gettext_noop ("The attribute VALUE"), | ||
857 | &attr_value), | ||
858 | GNUNET_GETOPT_option_string ('e', | ||
859 | "ego", | ||
860 | "EGO", | ||
861 | gettext_noop ("The EGO to use"), | ||
862 | &ego_name), | ||
863 | GNUNET_GETOPT_option_string ('r', | ||
864 | "rp", | ||
865 | "RP", | ||
866 | gettext_noop ( | ||
867 | "Specify the relying party for issue"), | ||
868 | &rp), | ||
869 | GNUNET_GETOPT_option_string ('U', | ||
870 | "rpuri", | ||
871 | "RPURI", | ||
872 | gettext_noop ( | ||
873 | "Specify the relying party URI for a ticket to consume"), | ||
874 | &ex_rp_uri), | ||
875 | GNUNET_GETOPT_option_flag ('D', | ||
876 | "dump", | ||
877 | gettext_noop ("List attributes for EGO"), | ||
878 | &list), | ||
879 | GNUNET_GETOPT_option_flag ('A', | ||
880 | "credentials", | ||
881 | gettext_noop ("List credentials for EGO"), | ||
882 | &list_credentials), | ||
883 | GNUNET_GETOPT_option_string ('I', | ||
884 | "credential-id", | ||
885 | "CREDENTIAL_ID", | ||
886 | gettext_noop ( | ||
887 | "Credential to use for attribute"), | ||
888 | &credential_id), | ||
889 | GNUNET_GETOPT_option_string ('N', | ||
890 | "credential-name", | ||
891 | "NAME", | ||
892 | gettext_noop ("Credential name"), | ||
893 | &credential_name), | ||
894 | GNUNET_GETOPT_option_string ('i', | ||
895 | "issue", | ||
896 | "A1,A2,...", | ||
897 | gettext_noop ( | ||
898 | "Issue a ticket for a set of attributes separated by comma"), | ||
899 | &issue_attrs), | ||
900 | GNUNET_GETOPT_option_string ('C', | ||
901 | "consume", | ||
902 | "TICKET", | ||
903 | gettext_noop ("Consume a ticket"), | ||
904 | &consume_ticket), | ||
905 | GNUNET_GETOPT_option_string ('R', | ||
906 | "revoke", | ||
907 | "TICKET", | ||
908 | gettext_noop ("Revoke a ticket"), | ||
909 | &revoke_ticket), | ||
910 | GNUNET_GETOPT_option_string ('t', | ||
911 | "type", | ||
912 | "TYPE", | ||
913 | gettext_noop ("Type of attribute"), | ||
914 | &type_str), | ||
915 | GNUNET_GETOPT_option_string ('u', | ||
916 | "credential-type", | ||
917 | "TYPE", | ||
918 | gettext_noop ("Type of credential"), | ||
919 | &credential_type), | ||
920 | GNUNET_GETOPT_option_flag ('T', | ||
921 | "tickets", | ||
922 | gettext_noop ("List tickets of ego"), | ||
923 | &list_tickets), | ||
924 | GNUNET_GETOPT_option_relative_time ('E', | ||
925 | "expiration", | ||
926 | "INTERVAL", | ||
927 | gettext_noop ( | ||
928 | "Expiration interval of the attribute"), | ||
929 | &exp_interval), | ||
930 | |||
931 | GNUNET_GETOPT_OPTION_END | ||
932 | }; | ||
933 | if (GNUNET_OK != GNUNET_PROGRAM_run (argc, | ||
934 | argv, | ||
935 | "gnunet-reclaim", | ||
936 | _ ("re:claimID command line tool"), | ||
937 | options, | ||
938 | &run, | ||
939 | NULL)) | ||
940 | return 1; | ||
941 | else | ||
942 | return ret; | ||
943 | } | ||
diff --git a/src/cli/reclaim/meson.build b/src/cli/reclaim/meson.build new file mode 100644 index 000000000..53ce13edb --- /dev/null +++ b/src/cli/reclaim/meson.build | |||
@@ -0,0 +1,22 @@ | |||
1 | executable ('gnunet-reclaim', | ||
2 | 'gnunet-reclaim.c', | ||
3 | dependencies: [libgnunetreclaim_dep, | ||
4 | libgnunetidentity_dep, | ||
5 | libgnunetutil_dep], | ||
6 | include_directories: [incdir, configuration_inc], | ||
7 | install: true, | ||
8 | install_dir: get_option('bindir')) | ||
9 | executable ('gnunet-did', | ||
10 | 'gnunet-did.c', | ||
11 | dependencies: [libgnunetreclaim_dep, | ||
12 | libgnunetdid_dep, | ||
13 | libgnunetgns_dep, | ||
14 | libgnunetnamestore_dep, | ||
15 | libgnunetidentity_dep, | ||
16 | libgnunetutil_dep], | ||
17 | include_directories: [incdir, | ||
18 | configuration_inc, | ||
19 | include_directories ('../../service/reclaim')], | ||
20 | install: true, | ||
21 | install_dir: get_option('bindir')) | ||
22 | |||
diff --git a/src/cli/reclaim/test_reclaim.conf b/src/cli/reclaim/test_reclaim.conf new file mode 100644 index 000000000..a8d2c808a --- /dev/null +++ b/src/cli/reclaim/test_reclaim.conf | |||
@@ -0,0 +1,44 @@ | |||
1 | @INLINE@ test_reclaim_defaults.conf | ||
2 | |||
3 | [PATHS] | ||
4 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-reclaim-peer-1/ | ||
5 | |||
6 | [dht] | ||
7 | START_ON_DEMAND = YES | ||
8 | |||
9 | [rest] | ||
10 | START_ON_DEMAND = YES | ||
11 | # PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/restlog | ||
12 | BASIC_AUTH_ENABLED = NO | ||
13 | |||
14 | [transport] | ||
15 | PLUGINS = | ||
16 | |||
17 | [zonemaster] | ||
18 | START_ON_DEMAND = YES | ||
19 | IMMEDIATE_START = YES | ||
20 | |||
21 | [reclaim] | ||
22 | IMMEDIATE_START = YES | ||
23 | START_ON_DEMAND = YES | ||
24 | TICKET_REFRESH_INTERVAL = 1 h | ||
25 | #PREFIX = valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --log-file=$GNUNET_TMP/idplog | ||
26 | |||
27 | [gns] | ||
28 | #PREFIX = valgrind --leak-check=full --track-origins=yes | ||
29 | START_ON_DEMAND = YES | ||
30 | AUTO_IMPORT_PKEY = YES | ||
31 | MAX_PARALLEL_BACKGROUND_QUERIES = 10 | ||
32 | DEFAULT_LOOKUP_TIMEOUT = 15 s | ||
33 | RECORD_PUT_INTERVAL = 1 h | ||
34 | ZONE_PUBLISH_TIME_WINDOW = 1 h | ||
35 | DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 | ||
36 | |||
37 | [reclaim-rest-plugin] | ||
38 | expiration_time = 3600 | ||
39 | JWT_SECRET = secret | ||
40 | OIDC_USERINFO_CONSUME_TIMEOUT = 5s | ||
41 | OIDC_DIR = $GNUNET_DATA_HOME/oidc | ||
42 | OIDC_CLIENT_HMAC_SECRET = secret | ||
43 | OIDC_JSON_WEB_ALGORITHM = RS256 | ||
44 | ADDRESS = https://ui.reclaim/#/login | ||
diff --git a/src/cli/reclaim/test_reclaim.sh b/src/cli/reclaim/test_reclaim.sh new file mode 100755 index 000000000..da93b10f7 --- /dev/null +++ b/src/cli/reclaim/test_reclaim.sh | |||
@@ -0,0 +1,31 @@ | |||
1 | #!/bin/sh | ||
2 | #trap "gnunet-arm -e -c test_reclaim_lookup.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | # (1) PKEY1.user -> PKEY2.resu.user | ||
19 | # (2) PKEY2.resu -> PKEY3 | ||
20 | # (3) PKEY3.user -> PKEY4 | ||
21 | |||
22 | |||
23 | which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
24 | |||
25 | TEST_ATTR="test" | ||
26 | gnunet-arm -s -c test_reclaim.conf | ||
27 | gnunet-identity -C testego -c test_reclaim.conf | ||
28 | valgrind gnunet-reclaim -e testego -a email -V john@doe.gnu -c test_reclaim.conf | ||
29 | gnunet-reclaim -e testego -a name -V John -c test_reclaim.conf | ||
30 | gnunet-reclaim -e testego -D -c test_reclaim.conf | ||
31 | gnunet-arm -e -c test_reclaim.conf | ||
diff --git a/src/cli/reclaim/test_reclaim_attribute.sh b/src/cli/reclaim/test_reclaim_attribute.sh new file mode 100755 index 000000000..17f7863d4 --- /dev/null +++ b/src/cli/reclaim/test_reclaim_attribute.sh | |||
@@ -0,0 +1,40 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_reclaim.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | # (1) PKEY1.user -> PKEY2.resu.user | ||
19 | # (2) PKEY2.resu -> PKEY3 | ||
20 | # (3) PKEY3.user -> PKEY4 | ||
21 | |||
22 | |||
23 | which timeout &> /dev/null && DO_TIMEOUT="timeout 30" | ||
24 | |||
25 | TEST_ATTR="test" | ||
26 | gnunet-arm -s -c test_reclaim.conf | ||
27 | #gnunet-arm -i rest -c test_reclaim.conf | ||
28 | gnunet-identity -C testego -c test_reclaim.conf | ||
29 | gnunet-identity -C rpego -c test_reclaim.conf | ||
30 | TEST_KEY=$(gnunet-identity -d -e testego -q -c test_reclaim.conf) | ||
31 | gnunet-reclaim -e testego -a email -V john@doe.gnu -c test_reclaim.conf | ||
32 | gnunet-reclaim -e testego -a name -V John -c test_reclaim.conf | ||
33 | if test $? != 0 | ||
34 | then | ||
35 | echo "Failed." | ||
36 | exit 1 | ||
37 | fi | ||
38 | |||
39 | #curl localhost:7776/reclaim/attributes/testego | ||
40 | gnunet-arm -e -c test_reclaim.conf | ||
diff --git a/src/cli/reclaim/test_reclaim_consume.sh b/src/cli/reclaim/test_reclaim_consume.sh new file mode 100755 index 000000000..00076fbf8 --- /dev/null +++ b/src/cli/reclaim/test_reclaim_consume.sh | |||
@@ -0,0 +1,51 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_reclaim.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1>/dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | # (1) PKEY1.user -> PKEY2.resu.user | ||
19 | # (2) PKEY2.resu -> PKEY3 | ||
20 | # (3) PKEY3.user -> PKEY4 | ||
21 | |||
22 | |||
23 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
24 | |||
25 | TEST_ATTR="test" | ||
26 | gnunet-arm -s -c test_reclaim.conf | ||
27 | #gnunet-arm -i rest -c test_reclaim.conf | ||
28 | gnunet-arm -I | ||
29 | gnunet-identity -C testego -c test_reclaim.conf | ||
30 | gnunet-identity -C rpego -c test_reclaim.conf | ||
31 | SUBJECT_KEY=$(gnunet-identity -d -e rpego -q -c test_reclaim.conf) | ||
32 | TEST_KEY=$(gnunet-identity -d -e testego -q -c test_reclaim.conf) | ||
33 | gnunet-reclaim -e testego -a email -V john@doe.gnu -c test_reclaim.conf | ||
34 | gnunet-reclaim -e testego -a name -V John -c test_reclaim.conf | ||
35 | TICKET=$(gnunet-reclaim -e testego -U "urn:gns:$TEST_KEY" -i "email,name" -r $SUBJECT_KEY -c test_reclaim.conf | awk '{print $1}') | ||
36 | echo "Ticket: $TICKET" | ||
37 | gnunet-gns -u $TICKET -c test_reclaim.conf | ||
38 | gnunet-namestore -z testego -D -c test_reclaim.conf | ||
39 | gnunet-identity -d -c test_reclaim.conf | ||
40 | sleep 1 | ||
41 | gnunet-reclaim -e rpego -U "urn:gns:$TEST_KEY" -C $TICKET -c test_reclaim.conf | ||
42 | |||
43 | RES=$? | ||
44 | gnunet-identity -D testego -c test_reclaim.conf | ||
45 | gnunet-identity -D rpego -c test_reclaim.conf | ||
46 | gnunet-arm -e -c test_reclaim.conf | ||
47 | if test $RES != 0 | ||
48 | then | ||
49 | echo "Failed." | ||
50 | fi | ||
51 | |||
diff --git a/src/cli/reclaim/test_reclaim_defaults.conf b/src/cli/reclaim/test_reclaim_defaults.conf new file mode 100644 index 000000000..02372563d --- /dev/null +++ b/src/cli/reclaim/test_reclaim_defaults.conf | |||
@@ -0,0 +1,24 @@ | |||
1 | @INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf | ||
2 | |||
3 | [PATHS] | ||
4 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-idp-testing/ | ||
5 | |||
6 | [namestore-sqlite] | ||
7 | FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db | ||
8 | |||
9 | [namecache-sqlite] | ||
10 | FILENAME=$GNUNET_TEST_HOME/namecache/namecache.db | ||
11 | |||
12 | [identity] | ||
13 | # Directory where we store information about our egos | ||
14 | EGODIR = $GNUNET_TEST_HOME/identity/egos/ | ||
15 | |||
16 | [dhtcache] | ||
17 | DATABASE = heap | ||
18 | |||
19 | [transport] | ||
20 | PLUGINS = tcp | ||
21 | |||
22 | [transport-tcp] | ||
23 | BINDTO = 127.0.0.1 | ||
24 | |||
diff --git a/src/cli/reclaim/test_reclaim_issue.sh b/src/cli/reclaim/test_reclaim_issue.sh new file mode 100755 index 000000000..39e614d19 --- /dev/null +++ b/src/cli/reclaim/test_reclaim_issue.sh | |||
@@ -0,0 +1,42 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_reclaim.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | # (1) PKEY1.user -> PKEY2.resu.user | ||
19 | # (2) PKEY2.resu -> PKEY3 | ||
20 | # (3) PKEY3.user -> PKEY4 | ||
21 | |||
22 | |||
23 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
24 | |||
25 | TEST_ATTR="test" | ||
26 | gnunet-arm -s -c test_reclaim.conf | ||
27 | #gnunet-arm -i rest -c test_reclaim.conf | ||
28 | gnunet-identity -C testego -c test_reclaim.conf | ||
29 | gnunet-identity -C rpego -c test_reclaim.conf | ||
30 | SUBJECT_KEY=$(gnunet-identity -d -e rpego -q -c test_reclaim.conf) | ||
31 | TEST_KEY=$(gnunet-identity -d -e testego -q -c test_reclaim.conf) | ||
32 | gnunet-reclaim -e testego -a email -V john@doe.gnu -c test_reclaim.conf | ||
33 | gnunet-reclaim -e testego -a name -V John -c test_reclaim.conf | ||
34 | #gnunet-reclaim -e testego -D -c test_reclaim.conf | ||
35 | gnunet-reclaim -e testego -u "urn:gns:$TEST_KEY" -i "email,name" -r $SUBJECT_KEY -c test_reclaim.conf > /dev/null 2>&1 | ||
36 | if test $? != 0 | ||
37 | then | ||
38 | echo "Failed." | ||
39 | exit 1 | ||
40 | fi | ||
41 | #curl http://localhost:7776/reclaim/attributes/testego | ||
42 | gnunet-arm -e -c test_reclaim.conf | ||
diff --git a/src/cli/reclaim/test_reclaim_oidc.sh b/src/cli/reclaim/test_reclaim_oidc.sh new file mode 100755 index 000000000..cdea61a03 --- /dev/null +++ b/src/cli/reclaim/test_reclaim_oidc.sh | |||
@@ -0,0 +1,57 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_reclaim.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1>/dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | |||
20 | RES=0 | ||
21 | TEST_ATTR="test" | ||
22 | REDIRECT_URI="https://example.gns.alt/my_cb" | ||
23 | SCOPE="\"openid email name\"" | ||
24 | gnunet-arm -s -c test_reclaim.conf | ||
25 | gnunet-arm -i rest -c test_reclaim.conf | ||
26 | gnunet-arm -I | ||
27 | gnunet-identity -C testego -c test_reclaim.conf | ||
28 | gnunet-identity -C rpego -c test_reclaim.conf | ||
29 | TEST_KEY=$(gnunet-identity -d -e rpego -q -c test_reclaim.conf) | ||
30 | SUBJECT_KEY=$(gnunet-identity -d -e testego -q -c test_reclaim.conf) | ||
31 | gnunet-reclaim -e testego -a email -V john@doe.gnu -c test_reclaim.conf | ||
32 | gnunet-reclaim -e testego -a name -V John -c test_reclaim.conf | ||
33 | |||
34 | # Register client | ||
35 | gnunet-namestore -z rpego -a -n @ -t RECLAIM_OIDC_CLIENT -V "My RP" -e 1d -p -c test_reclaim.conf | ||
36 | gnunet-namestore -z rpego -a -n @ -t RECLAIM_OIDC_REDIRECT -V $REDIRECT_URI -e 1d -p -c test_reclaim.conf | ||
37 | |||
38 | gnunet-gns -u @.$TEST_KEY -t RECLAIM_OIDC_REDIRECT -c test_reclaim.conf | ||
39 | curl -v -X POST -H -v "http://localhost:7776/openid/login" --data "{\"identity\": \"$SUBJECT_KEY\"}" | ||
40 | |||
41 | PKCE_CHALLENGE=$(echo -n secret | openssl dgst -binary -sha256 | openssl base64 | sed 's/\=//g' | sed 's/+/-/g' | sed 's/\//_/g') | ||
42 | |||
43 | CODE=$(curl -H "Cookie: Identity=$SUBJECT_KEY" "http://localhost:7776/openid/authorize?client_id=$TEST_KEY&response_type=code&redirect_uri=$REDIRECT_URI&scope=openid&claims=%7B%22userinfo%22%3A%20%7B%22email%22%3A%20%7B%22essential%22%20%20%20%20%3A%20true%7D%7D%2C%22id_token%22%3A%20%7B%22email%22%3A%20%7B%22essential%22%3A%20true%7D%7D%7D&state=xyz&code_challenge=$PKCE_CHALLENGE&code_challenge_method=S256" \ | ||
44 | -sS -D - -o /dev/null | grep "Location: " | cut -d" " -f2 | cut -d"?" -f2 | cut -d"&" -f1 | cut -d"=" -f2) | ||
45 | |||
46 | echo "Code: $CODE" | ||
47 | |||
48 | curl -v -X POST -u$TEST_KEY:"secret" "http://localhost:7776/openid/token?client_id=$TEST_KEY&response_type=code&redirect_uri=$REDIRECT_URI&scope=openid&claims=%7B%22userinfo%22%3A%20%7B%22email%22%3A%20%7B%22essential%22%20%20%20%20%3A%20true%7D%7D%2C%22id_token%22%3A%20%7B%22email%22%3A%20%7B%22essential%22%3A%20true%7D%7D%7D&state=xyz&grant_type=authorization_code&code=$CODE&code_verifier=secret" | ||
49 | |||
50 | gnunet-identity -D testego -c test_reclaim.conf | ||
51 | gnunet-identity -D rpego -c test_reclaim.conf | ||
52 | gnunet-arm -e -c test_reclaim.conf | ||
53 | if test $RES != 0 | ||
54 | then | ||
55 | echo "Failed." | ||
56 | fi | ||
57 | |||
diff --git a/src/cli/reclaim/test_reclaim_revoke.sh b/src/cli/reclaim/test_reclaim_revoke.sh new file mode 100755 index 000000000..da091a1ee --- /dev/null +++ b/src/cli/reclaim/test_reclaim_revoke.sh | |||
@@ -0,0 +1,65 @@ | |||
1 | #!/bin/bash | ||
2 | trap "gnunet-arm -e -c test_reclaim.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_reclaim.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | # (1) PKEY1.user -> PKEY2.resu.user | ||
19 | # (2) PKEY2.resu -> PKEY3 | ||
20 | # (3) PKEY3.user -> PKEY4 | ||
21 | |||
22 | |||
23 | which timeout >/dev/null 2&>1 && DO_TIMEOUT="timeout 30" | ||
24 | |||
25 | TEST_ATTR="test" | ||
26 | gnunet-arm -s -c test_reclaim.conf >/dev/null 2&>1 | ||
27 | gnunet-identity -C alice -c test_reclaim.conf | ||
28 | gnunet-identity -C bob -c test_reclaim.conf | ||
29 | gnunet-identity -C eve -c test_reclaim.conf | ||
30 | ALICE_KEY=$(gnunet-identity -d -e alice -q -c test_reclaim.conf) | ||
31 | BOB_KEY=$(gnunet-identity -d -e bob -q -c test_reclaim.conf) | ||
32 | EVE_KEY=$(gnunet-identity -d -e eve -q -c test_reclaim.conf) | ||
33 | gnunet-reclaim -e alice -E 15s -a email -V john@doe.gnu -c test_reclaim.conf | ||
34 | gnunet-reclaim -e alice -E 15s -a name -V John -c test_reclaim.conf | ||
35 | TICKET_BOB=$(gnunet-reclaim -e alice -i "email,name" -r $BOB_KEY -c test_reclaim.conf | awk '{print $1}') | ||
36 | #gnunet-reclaim -e bob -C $TICKET_BOB -c test_reclaim.conf | ||
37 | TICKET_EVE=$(gnunet-reclaim -e alice -i "email" -r $EVE_KEY -c test_reclaim.conf | awk '{print $1}') | ||
38 | gnunet-namestore -z alice -D | ||
39 | echo "Revoking $TICKET" | ||
40 | gnunet-reclaim -e alice -R $TICKET_EVE -c test_reclaim.conf | ||
41 | gnunet-namestore -z alice -D | ||
42 | sleep 16 | ||
43 | echo "Consuming $TICKET" | ||
44 | |||
45 | gnunet-reclaim -e eve -C $TICKET_EVE -c test_reclaim.conf | ||
46 | if test $? = 0 | ||
47 | then | ||
48 | echo "Eve can still resolve attributes..." | ||
49 | gnunet-arm -e -c test_reclaim.conf | ||
50 | exit 1 | ||
51 | fi | ||
52 | |||
53 | gnunet-arm -e -c test_reclaim.conf | ||
54 | gnunet-arm -s -c test_reclaim.conf >/dev/null 2&>1 | ||
55 | |||
56 | gnunet-reclaim -e bob -C $TICKET_BOB -c test_reclaim.conf | ||
57 | #gnunet-reclaim -e bob -C $TICKET_BOB -c test_reclaim.conf >/dev/null 2&>1 | ||
58 | if test $? != 0 | ||
59 | then | ||
60 | echo "Bob cannot resolve attributes..." | ||
61 | gnunet-arm -e -c test_reclaim.conf | ||
62 | exit 1 | ||
63 | fi | ||
64 | |||
65 | gnunet-arm -e -c test_reclaim.conf | ||
diff --git a/src/cli/revocation/Makefile.am b/src/cli/revocation/Makefile.am new file mode 100644 index 000000000..ffd76effa --- /dev/null +++ b/src/cli/revocation/Makefile.am | |||
@@ -0,0 +1,52 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS = --coverage -O0 | ||
8 | XLIB = -lgcov | ||
9 | endif | ||
10 | |||
11 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
12 | |||
13 | libexecdir= $(pkglibdir)/libexec/ | ||
14 | |||
15 | bin_PROGRAMS = \ | ||
16 | gnunet-revocation | ||
17 | |||
18 | gnunet_revocation_SOURCES = \ | ||
19 | gnunet-revocation.c | ||
20 | gnunet_revocation_LDADD = \ | ||
21 | $(top_builddir)/src/service/revocation/libgnunetrevocation.la \ | ||
22 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
23 | $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ | ||
24 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
25 | $(GN_LIBINTL) | ||
26 | |||
27 | gnunet_revocation_tvg_SOURCES = \ | ||
28 | gnunet-revocation-tvg.c | ||
29 | gnunet_revocation_tvg_LDADD = \ | ||
30 | $(top_builddir)/src/service/revocation/libgnunetrevocation.la \ | ||
31 | $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ | ||
32 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
33 | $(GN_LIBINTL) | ||
34 | |||
35 | noinst_PROGRAMS = \ | ||
36 | gnunet-revocation-tvg | ||
37 | |||
38 | check_SCRIPTS = \ | ||
39 | #test_local_revocation.py | ||
40 | |||
41 | if ENABLE_TEST_RUN | ||
42 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
43 | TESTS = \ | ||
44 | $(check_SCRIPTS) \ | ||
45 | $(check_PROGRAMS) | ||
46 | endif | ||
47 | |||
48 | test_local_revocation.py: test_local_revocation.py.in Makefile | ||
49 | $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/test_local_revocation.py.in > test_local_revocation.py | ||
50 | chmod +x test_local_revocation.py | ||
51 | |||
52 | EXTRA_DIST = test_local_revocation.py.in | ||
diff --git a/src/cli/revocation/gnunet-revocation-tvg.c b/src/cli/revocation/gnunet-revocation-tvg.c new file mode 100644 index 000000000..5c2bfbe45 --- /dev/null +++ b/src/cli/revocation/gnunet-revocation-tvg.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2020 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-revocation-tvg.c | ||
23 | * @brief Generate test vectors for revocation. | ||
24 | * @author Martin Schanzenbach | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_signatures.h" | ||
29 | #include "gnunet_revocation_service.h" | ||
30 | #include "gnunet_testing_lib.h" | ||
31 | // FIXME try to avoid this include somehow | ||
32 | #include "../../lib/gnsrecord/gnsrecord_crypto.h" | ||
33 | #include <inttypes.h> | ||
34 | |||
35 | #define TEST_EPOCHS 2 | ||
36 | #define TEST_DIFFICULTY 5 | ||
37 | |||
38 | static char*d_pkey = | ||
39 | "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70"; | ||
40 | |||
41 | static char *d_edkey = | ||
42 | "5af7020ee19160328832352bbc6a68a8d71a7cbe1b929969a7c66d415a0d8f65"; | ||
43 | |||
44 | int | ||
45 | parsehex (char *src, char *dst, size_t dstlen, int invert) | ||
46 | { | ||
47 | char *line = src; | ||
48 | char *data = line; | ||
49 | int off; | ||
50 | int read_byte; | ||
51 | int data_len = 0; | ||
52 | |||
53 | while (sscanf (data, " %02x%n", &read_byte, &off) == 1) | ||
54 | { | ||
55 | if (invert) | ||
56 | dst[dstlen - 1 - data_len++] = read_byte; | ||
57 | else | ||
58 | dst[data_len++] = read_byte; | ||
59 | data += off; | ||
60 | } | ||
61 | return data_len; | ||
62 | } | ||
63 | |||
64 | |||
65 | static void | ||
66 | print_bytes_ (void *buf, | ||
67 | size_t buf_len, | ||
68 | int fold, | ||
69 | int in_be) | ||
70 | { | ||
71 | int i; | ||
72 | |||
73 | for (i = 0; i < buf_len; i++) | ||
74 | { | ||
75 | if (0 != i) | ||
76 | { | ||
77 | if ((0 != fold) && (i % fold == 0)) | ||
78 | printf ("\n "); | ||
79 | else | ||
80 | printf (" "); | ||
81 | } | ||
82 | else | ||
83 | { | ||
84 | printf (" "); | ||
85 | } | ||
86 | if (in_be) | ||
87 | printf ("%02x", ((unsigned char*) buf)[buf_len - 1 - i]); | ||
88 | else | ||
89 | printf ("%02x", ((unsigned char*) buf)[i]); | ||
90 | } | ||
91 | printf ("\n"); | ||
92 | } | ||
93 | |||
94 | |||
95 | static void | ||
96 | print_bytes (void *buf, | ||
97 | size_t buf_len, | ||
98 | int fold) | ||
99 | { | ||
100 | print_bytes_ (buf, buf_len, fold, 0); | ||
101 | } | ||
102 | |||
103 | |||
104 | static void | ||
105 | run_with_key (struct GNUNET_CRYPTO_PrivateKey *id_priv) | ||
106 | { | ||
107 | struct GNUNET_CRYPTO_PublicKey id_pub; | ||
108 | struct GNUNET_GNSRECORD_PowP *pow; | ||
109 | struct GNUNET_GNSRECORD_PowCalculationHandle *ph; | ||
110 | struct GNUNET_TIME_Relative exp; | ||
111 | char ztld[128]; | ||
112 | ssize_t key_len; | ||
113 | |||
114 | GNUNET_CRYPTO_key_get_public (id_priv, | ||
115 | &id_pub); | ||
116 | GNUNET_STRINGS_data_to_string (&id_pub, | ||
117 | GNUNET_CRYPTO_public_key_get_length ( | ||
118 | &id_pub), | ||
119 | ztld, | ||
120 | sizeof (ztld)); | ||
121 | fprintf (stdout, "\n"); | ||
122 | fprintf (stdout, "Zone identifier (ztype|zkey):\n"); | ||
123 | key_len = GNUNET_CRYPTO_public_key_get_length (&id_pub); | ||
124 | GNUNET_assert (0 < key_len); | ||
125 | print_bytes (&id_pub, key_len, 8); | ||
126 | fprintf (stdout, "\n"); | ||
127 | fprintf (stdout, "Encoded zone identifier (zkl = zTLD):\n"); | ||
128 | fprintf (stdout, "%s\n", ztld); | ||
129 | fprintf (stdout, "\n"); | ||
130 | pow = GNUNET_malloc (GNUNET_MAX_POW_SIZE); | ||
131 | GNUNET_GNSRECORD_pow_init (id_priv, | ||
132 | pow); | ||
133 | ph = GNUNET_GNSRECORD_pow_start (pow, | ||
134 | TEST_EPOCHS, | ||
135 | TEST_DIFFICULTY); | ||
136 | fprintf (stdout, "Difficulty (%d base difficulty + %d epochs): %d\n\n", | ||
137 | TEST_DIFFICULTY, | ||
138 | TEST_EPOCHS, | ||
139 | TEST_DIFFICULTY + TEST_EPOCHS); | ||
140 | uint64_t pow_passes = 0; | ||
141 | while (GNUNET_YES != GNUNET_GNSRECORD_pow_round (ph)) | ||
142 | { | ||
143 | pow_passes++; | ||
144 | } | ||
145 | struct GNUNET_GNSRECORD_SignaturePurposePS *purp; | ||
146 | purp = GNR_create_signature_message (pow); | ||
147 | fprintf (stdout, "Signed message:\n"); | ||
148 | print_bytes (purp, | ||
149 | ntohl (purp->purpose.size), | ||
150 | 8); | ||
151 | printf ("\n"); | ||
152 | GNUNET_free (purp); | ||
153 | |||
154 | exp = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, | ||
155 | TEST_EPOCHS); | ||
156 | GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_check_pow (pow, | ||
157 | TEST_DIFFICULTY, | ||
158 | exp)); | ||
159 | fprintf (stdout, "Proof:\n"); | ||
160 | print_bytes (pow, | ||
161 | GNUNET_GNSRECORD_proof_get_size (pow), | ||
162 | 8); | ||
163 | GNUNET_free (ph); | ||
164 | |||
165 | } | ||
166 | |||
167 | |||
168 | /** | ||
169 | * Main function that will be run. | ||
170 | * | ||
171 | * @param cls closure | ||
172 | * @param args remaining command-line arguments | ||
173 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
174 | * @param cfg configuration | ||
175 | */ | ||
176 | static void | ||
177 | run (void *cls, | ||
178 | char *const *args, | ||
179 | const char *cfgfile, | ||
180 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
181 | { | ||
182 | struct GNUNET_CRYPTO_PrivateKey id_priv; | ||
183 | |||
184 | id_priv.type = htonl (GNUNET_PUBLIC_KEY_TYPE_ECDSA); | ||
185 | parsehex (d_pkey,(char*) &id_priv.ecdsa_key, sizeof (id_priv.ecdsa_key), 1); | ||
186 | |||
187 | fprintf (stdout, "Zone private key (d, big-endian):\n"); | ||
188 | print_bytes_ (&id_priv.ecdsa_key, sizeof(id_priv.ecdsa_key), 8, 1); | ||
189 | run_with_key (&id_priv); | ||
190 | printf ("\n"); | ||
191 | id_priv.type = htonl (GNUNET_PUBLIC_KEY_TYPE_EDDSA); | ||
192 | parsehex (d_edkey,(char*) &id_priv.eddsa_key, sizeof (id_priv.eddsa_key), 0); | ||
193 | |||
194 | fprintf (stdout, "Zone private key (d):\n"); | ||
195 | print_bytes (&id_priv.eddsa_key, sizeof(id_priv.eddsa_key), 8); | ||
196 | run_with_key (&id_priv); | ||
197 | } | ||
198 | |||
199 | |||
200 | /** | ||
201 | * The main function of the test vector generation tool. | ||
202 | * | ||
203 | * @param argc number of arguments from the command line | ||
204 | * @param argv command line arguments | ||
205 | * @return 0 ok, 1 on error | ||
206 | */ | ||
207 | int | ||
208 | main (int argc, | ||
209 | char *const *argv) | ||
210 | { | ||
211 | const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
212 | GNUNET_GETOPT_OPTION_END | ||
213 | }; | ||
214 | |||
215 | GNUNET_assert (GNUNET_OK == | ||
216 | GNUNET_log_setup ("gnunet-revocation-tvg", | ||
217 | "INFO", | ||
218 | NULL)); | ||
219 | if (GNUNET_OK != | ||
220 | GNUNET_PROGRAM_run (argc, argv, | ||
221 | "gnunet-revocation-tvg", | ||
222 | "Generate test vectors for revocation", | ||
223 | options, | ||
224 | &run, NULL)) | ||
225 | return 1; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | |||
230 | /* end of gnunet-revocation-tvg.c */ | ||
diff --git a/src/cli/revocation/gnunet-revocation.c b/src/cli/revocation/gnunet-revocation.c new file mode 100644 index 000000000..add9a003b --- /dev/null +++ b/src/cli/revocation/gnunet-revocation.c | |||
@@ -0,0 +1,581 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file revocation/gnunet-revocation.c | ||
23 | * @brief tool for revoking public keys | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_gnsrecord_lib.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_revocation_service.h" | ||
30 | |||
31 | /** | ||
32 | * Pow passes | ||
33 | */ | ||
34 | static unsigned int pow_passes = 1; | ||
35 | |||
36 | /** | ||
37 | * Final status code. | ||
38 | */ | ||
39 | static int ret; | ||
40 | |||
41 | /** | ||
42 | * Was "-p" specified? | ||
43 | */ | ||
44 | static int perform; | ||
45 | |||
46 | /** | ||
47 | * -f option. | ||
48 | */ | ||
49 | static char *filename; | ||
50 | |||
51 | /** | ||
52 | * -R option | ||
53 | */ | ||
54 | static char *revoke_ego; | ||
55 | |||
56 | /** | ||
57 | * -t option. | ||
58 | */ | ||
59 | static char *test_ego; | ||
60 | |||
61 | /** | ||
62 | * -e option. | ||
63 | */ | ||
64 | static unsigned int epochs = 1; | ||
65 | |||
66 | /** | ||
67 | * Handle for revocation query. | ||
68 | */ | ||
69 | static struct GNUNET_REVOCATION_Query *q; | ||
70 | |||
71 | /** | ||
72 | * Handle for revocation. | ||
73 | */ | ||
74 | static struct GNUNET_REVOCATION_Handle *h; | ||
75 | |||
76 | /** | ||
77 | * Handle for our ego lookup. | ||
78 | */ | ||
79 | static struct GNUNET_IDENTITY_EgoLookup *el; | ||
80 | |||
81 | /** | ||
82 | * Our configuration. | ||
83 | */ | ||
84 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
85 | |||
86 | /** | ||
87 | * Number of matching bits required for revocation. | ||
88 | */ | ||
89 | static unsigned long long matching_bits; | ||
90 | |||
91 | /** | ||
92 | * Epoch length | ||
93 | */ | ||
94 | static struct GNUNET_TIME_Relative epoch_duration; | ||
95 | |||
96 | /** | ||
97 | * Task used for proof-of-work calculation. | ||
98 | */ | ||
99 | static struct GNUNET_SCHEDULER_Task *pow_task; | ||
100 | |||
101 | /** | ||
102 | * Proof-of-work object | ||
103 | */ | ||
104 | static struct GNUNET_GNSRECORD_PowP *proof_of_work; | ||
105 | |||
106 | /** | ||
107 | * Function run if the user aborts with CTRL-C. | ||
108 | * | ||
109 | * @param cls closure | ||
110 | */ | ||
111 | static void | ||
112 | do_shutdown (void *cls) | ||
113 | { | ||
114 | fprintf (stderr, "%s", _ ("Shutting down...\n")); | ||
115 | if (NULL != el) | ||
116 | { | ||
117 | GNUNET_IDENTITY_ego_lookup_cancel (el); | ||
118 | el = NULL; | ||
119 | } | ||
120 | if (NULL != q) | ||
121 | { | ||
122 | GNUNET_REVOCATION_query_cancel (q); | ||
123 | q = NULL; | ||
124 | } | ||
125 | if (NULL != h) | ||
126 | { | ||
127 | GNUNET_REVOCATION_revoke_cancel (h); | ||
128 | h = NULL; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Print the result from a revocation query. | ||
135 | * | ||
136 | * @param cls NULL | ||
137 | * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error | ||
138 | */ | ||
139 | static void | ||
140 | print_query_result (void *cls, int is_valid) | ||
141 | { | ||
142 | q = NULL; | ||
143 | switch (is_valid) | ||
144 | { | ||
145 | case GNUNET_YES: | ||
146 | fprintf (stdout, _ ("Key `%s' is valid\n"), test_ego); | ||
147 | break; | ||
148 | |||
149 | case GNUNET_NO: | ||
150 | fprintf (stdout, _ ("Key `%s' has been revoked\n"), test_ego); | ||
151 | break; | ||
152 | |||
153 | case GNUNET_SYSERR: | ||
154 | fprintf (stdout, "%s", _ ("Internal error\n")); | ||
155 | break; | ||
156 | |||
157 | default: | ||
158 | GNUNET_break (0); | ||
159 | break; | ||
160 | } | ||
161 | GNUNET_SCHEDULER_shutdown (); | ||
162 | } | ||
163 | |||
164 | |||
165 | /** | ||
166 | * Print the result from a revocation request. | ||
167 | * | ||
168 | * @param cls NULL | ||
169 | * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error | ||
170 | */ | ||
171 | static void | ||
172 | print_revocation_result (void *cls, int is_valid) | ||
173 | { | ||
174 | h = NULL; | ||
175 | switch (is_valid) | ||
176 | { | ||
177 | case GNUNET_YES: | ||
178 | if (NULL != revoke_ego) | ||
179 | fprintf (stdout, | ||
180 | _ ("Key for ego `%s' is still valid, revocation failed (!)\n"), | ||
181 | revoke_ego); | ||
182 | else | ||
183 | fprintf (stdout, "%s", _ ("Revocation failed (!)\n")); | ||
184 | break; | ||
185 | |||
186 | case GNUNET_NO: | ||
187 | if (NULL != revoke_ego) | ||
188 | fprintf (stdout, | ||
189 | _ ("Key for ego `%s' has been successfully revoked\n"), | ||
190 | revoke_ego); | ||
191 | else | ||
192 | fprintf (stdout, "%s", _ ("Revocation successful.\n")); | ||
193 | break; | ||
194 | |||
195 | case GNUNET_SYSERR: | ||
196 | fprintf (stdout, | ||
197 | "%s", | ||
198 | _ ("Internal error, key revocation might have failed\n")); | ||
199 | break; | ||
200 | |||
201 | default: | ||
202 | GNUNET_break (0); | ||
203 | break; | ||
204 | } | ||
205 | GNUNET_SCHEDULER_shutdown (); | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Perform the revocation. | ||
211 | */ | ||
212 | static void | ||
213 | perform_revocation () | ||
214 | { | ||
215 | h = GNUNET_REVOCATION_revoke (cfg, | ||
216 | proof_of_work, | ||
217 | &print_revocation_result, | ||
218 | NULL); | ||
219 | } | ||
220 | |||
221 | |||
222 | /** | ||
223 | * Write the current state of the revocation data | ||
224 | * to disk. | ||
225 | * | ||
226 | * @param rd data to sync | ||
227 | */ | ||
228 | static void | ||
229 | sync_pow () | ||
230 | { | ||
231 | size_t psize = GNUNET_GNSRECORD_proof_get_size (proof_of_work); | ||
232 | if ((NULL != filename) && | ||
233 | (GNUNET_OK != | ||
234 | GNUNET_DISK_fn_write (filename, | ||
235 | proof_of_work, | ||
236 | psize, | ||
237 | GNUNET_DISK_PERM_USER_READ | ||
238 | | GNUNET_DISK_PERM_USER_WRITE))) | ||
239 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", filename); | ||
240 | } | ||
241 | |||
242 | |||
243 | /** | ||
244 | * Perform the proof-of-work calculation. | ||
245 | * | ||
246 | * @param cls the `struct RevocationData` | ||
247 | */ | ||
248 | static void | ||
249 | calculate_pow_shutdown (void *cls) | ||
250 | { | ||
251 | struct GNUNET_GNSRECORD_PowCalculationHandle *ph = cls; | ||
252 | fprintf (stderr, "%s", _ ("Cancelling calculation.\n")); | ||
253 | sync_pow (); | ||
254 | if (NULL != pow_task) | ||
255 | { | ||
256 | GNUNET_SCHEDULER_cancel (pow_task); | ||
257 | pow_task = NULL; | ||
258 | } | ||
259 | if (NULL != ph) | ||
260 | GNUNET_GNSRECORD_pow_stop (ph); | ||
261 | } | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Perform the proof-of-work calculation. | ||
266 | * | ||
267 | * @param cls the `struct RevocationData` | ||
268 | */ | ||
269 | static void | ||
270 | calculate_pow (void *cls) | ||
271 | { | ||
272 | struct GNUNET_GNSRECORD_PowCalculationHandle *ph = cls; | ||
273 | size_t psize; | ||
274 | |||
275 | /* store temporary results */ | ||
276 | pow_task = NULL; | ||
277 | if (0 == (pow_passes % 128)) | ||
278 | sync_pow (); | ||
279 | /* actually do POW calculation */ | ||
280 | if (GNUNET_OK == GNUNET_GNSRECORD_pow_round (ph)) | ||
281 | { | ||
282 | psize = GNUNET_GNSRECORD_proof_get_size (proof_of_work); | ||
283 | if (NULL != filename) | ||
284 | { | ||
285 | (void) GNUNET_DISK_directory_remove (filename); | ||
286 | if (GNUNET_OK != | ||
287 | GNUNET_DISK_fn_write (filename, | ||
288 | proof_of_work, | ||
289 | psize, | ||
290 | GNUNET_DISK_PERM_USER_READ | ||
291 | | GNUNET_DISK_PERM_USER_WRITE)) | ||
292 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", filename); | ||
293 | } | ||
294 | if (perform) | ||
295 | { | ||
296 | perform_revocation (); | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | fprintf (stderr, "%s", "\n"); | ||
301 | fprintf (stderr, | ||
302 | _ ("Revocation certificate for `%s' stored in `%s'\n"), | ||
303 | revoke_ego, | ||
304 | filename); | ||
305 | GNUNET_SCHEDULER_shutdown (); | ||
306 | } | ||
307 | return; | ||
308 | } | ||
309 | pow_passes++; | ||
310 | pow_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, | ||
311 | &calculate_pow, | ||
312 | ph); | ||
313 | |||
314 | } | ||
315 | |||
316 | |||
317 | /** | ||
318 | * Function called with the result from the ego lookup. | ||
319 | * | ||
320 | * @param cls closure | ||
321 | * @param ego the ego, NULL if not found | ||
322 | */ | ||
323 | static void | ||
324 | ego_callback (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
325 | { | ||
326 | struct GNUNET_CRYPTO_PublicKey key; | ||
327 | const struct GNUNET_CRYPTO_PrivateKey *privkey; | ||
328 | struct GNUNET_GNSRECORD_PowCalculationHandle *ph = NULL; | ||
329 | size_t psize; | ||
330 | |||
331 | el = NULL; | ||
332 | if (NULL == ego) | ||
333 | { | ||
334 | fprintf (stdout, _ ("Ego `%s' not found.\n"), revoke_ego); | ||
335 | GNUNET_SCHEDULER_shutdown (); | ||
336 | return; | ||
337 | } | ||
338 | GNUNET_IDENTITY_ego_get_public_key (ego, &key); | ||
339 | privkey = GNUNET_IDENTITY_ego_get_private_key (ego); | ||
340 | proof_of_work = GNUNET_malloc (GNUNET_MAX_POW_SIZE); | ||
341 | if ((NULL != filename) && (GNUNET_YES == GNUNET_DISK_file_test (filename)) && | ||
342 | (0 < (psize = | ||
343 | GNUNET_DISK_fn_read (filename, proof_of_work, | ||
344 | GNUNET_MAX_POW_SIZE)))) | ||
345 | { | ||
346 | ssize_t ksize = GNUNET_CRYPTO_public_key_get_length (&key); | ||
347 | if (0 > ksize) | ||
348 | { | ||
349 | fprintf (stderr, | ||
350 | _ ("Error: Key is invalid\n")); | ||
351 | return; | ||
352 | } | ||
353 | if (((psize - sizeof (*proof_of_work)) < ksize) || // Key too small | ||
354 | (0 != memcmp (&proof_of_work[1], &key, ksize))) // Keys do not match | ||
355 | { | ||
356 | fprintf (stderr, | ||
357 | _ ("Error: revocation certificate in `%s' is not for `%s'\n"), | ||
358 | filename, | ||
359 | revoke_ego); | ||
360 | return; | ||
361 | } | ||
362 | if (GNUNET_YES == | ||
363 | GNUNET_GNSRECORD_check_pow (proof_of_work, | ||
364 | (unsigned int) matching_bits, | ||
365 | epoch_duration)) | ||
366 | { | ||
367 | fprintf (stderr, "%s", _ ("Revocation certificate ready\n")); | ||
368 | if (perform) | ||
369 | perform_revocation (); | ||
370 | else | ||
371 | GNUNET_SCHEDULER_shutdown (); | ||
372 | return; | ||
373 | } | ||
374 | /** | ||
375 | * Certificate not yet ready | ||
376 | */ | ||
377 | fprintf (stderr, | ||
378 | "%s", | ||
379 | _ ("Continuing calculation where left off...\n")); | ||
380 | ph = GNUNET_GNSRECORD_pow_start (proof_of_work, | ||
381 | epochs, | ||
382 | matching_bits); | ||
383 | } | ||
384 | fprintf (stderr, | ||
385 | "%s", | ||
386 | _ ("Revocation certificate not ready, calculating proof of work\n")); | ||
387 | if (NULL == ph) | ||
388 | { | ||
389 | GNUNET_GNSRECORD_pow_init (privkey, | ||
390 | proof_of_work); | ||
391 | ph = GNUNET_GNSRECORD_pow_start (proof_of_work, | ||
392 | epochs, /* Epochs */ | ||
393 | matching_bits); | ||
394 | } | ||
395 | pow_task = GNUNET_SCHEDULER_add_now (&calculate_pow, ph); | ||
396 | GNUNET_SCHEDULER_add_shutdown (&calculate_pow_shutdown, ph); | ||
397 | } | ||
398 | |||
399 | |||
400 | /** | ||
401 | * Main function that will be run by the scheduler. | ||
402 | * | ||
403 | * @param cls closure | ||
404 | * @param args remaining command-line arguments | ||
405 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
406 | * @param c configuration | ||
407 | */ | ||
408 | static void | ||
409 | run (void *cls, | ||
410 | char *const *args, | ||
411 | const char *cfgfile, | ||
412 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
413 | { | ||
414 | struct GNUNET_CRYPTO_PublicKey pk; | ||
415 | size_t psize; | ||
416 | |||
417 | cfg = c; | ||
418 | if (NULL != test_ego) | ||
419 | { | ||
420 | if (GNUNET_OK != | ||
421 | GNUNET_CRYPTO_public_key_from_string (test_ego, | ||
422 | &pk)) | ||
423 | { | ||
424 | fprintf (stderr, _ ("Public key `%s' malformed\n"), test_ego); | ||
425 | return; | ||
426 | } | ||
427 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
428 | q = GNUNET_REVOCATION_query (cfg, &pk, &print_query_result, NULL); | ||
429 | if (NULL != revoke_ego) | ||
430 | fprintf ( | ||
431 | stderr, | ||
432 | "%s", | ||
433 | _ ( | ||
434 | "Testing and revoking at the same time is not allowed, only executing test.\n")); | ||
435 | return; | ||
436 | } | ||
437 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, | ||
438 | "REVOCATION", | ||
439 | "WORKBITS", | ||
440 | &matching_bits)) | ||
441 | { | ||
442 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
443 | "REVOCATION", | ||
444 | "WORKBITS"); | ||
445 | return; | ||
446 | } | ||
447 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, | ||
448 | "REVOCATION", | ||
449 | "EPOCH_DURATION", | ||
450 | &epoch_duration)) | ||
451 | { | ||
452 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
453 | "REVOCATION", | ||
454 | "EPOCH_DURATION"); | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | if (NULL != revoke_ego) | ||
459 | { | ||
460 | if (! perform && (NULL == filename)) | ||
461 | { | ||
462 | fprintf (stderr, | ||
463 | "%s", | ||
464 | _ ("No filename to store revocation certificate given.\n")); | ||
465 | return; | ||
466 | } | ||
467 | /* main code here */ | ||
468 | el = GNUNET_IDENTITY_ego_lookup (cfg, revoke_ego, &ego_callback, NULL); | ||
469 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
470 | return; | ||
471 | } | ||
472 | if ((NULL != filename) && (perform)) | ||
473 | { | ||
474 | size_t bread; | ||
475 | proof_of_work = GNUNET_malloc (GNUNET_MAX_POW_SIZE); | ||
476 | if (0 < (bread = GNUNET_DISK_fn_read (filename, | ||
477 | proof_of_work, | ||
478 | GNUNET_MAX_POW_SIZE))) | ||
479 | { | ||
480 | fprintf (stderr, | ||
481 | _ ("Failed to read revocation certificate from `%s'\n"), | ||
482 | filename); | ||
483 | return; | ||
484 | } | ||
485 | psize = GNUNET_GNSRECORD_proof_get_size (proof_of_work); | ||
486 | if (bread != psize) | ||
487 | { | ||
488 | fprintf (stderr, | ||
489 | _ ("Revocation certificate corrupted in `%s'\n"), | ||
490 | filename); | ||
491 | return; | ||
492 | } | ||
493 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); | ||
494 | if (GNUNET_YES != | ||
495 | GNUNET_GNSRECORD_check_pow (proof_of_work, | ||
496 | (unsigned int) matching_bits, | ||
497 | epoch_duration)) | ||
498 | { | ||
499 | struct GNUNET_GNSRECORD_PowCalculationHandle *ph; | ||
500 | ph = GNUNET_GNSRECORD_pow_start (proof_of_work, | ||
501 | epochs, /* Epochs */ | ||
502 | matching_bits); | ||
503 | |||
504 | pow_task = GNUNET_SCHEDULER_add_now (&calculate_pow, ph); | ||
505 | GNUNET_SCHEDULER_add_shutdown (&calculate_pow_shutdown, ph); | ||
506 | return; | ||
507 | } | ||
508 | perform_revocation (); | ||
509 | return; | ||
510 | } | ||
511 | fprintf (stderr, "%s", _ ("No action specified. Nothing to do.\n")); | ||
512 | } | ||
513 | |||
514 | |||
515 | /** | ||
516 | * The main function of gnunet-revocation. | ||
517 | * | ||
518 | * @param argc number of arguments from the command line | ||
519 | * @param argv command line arguments | ||
520 | * @return 0 ok, 1 on error | ||
521 | */ | ||
522 | int | ||
523 | main (int argc, char *const *argv) | ||
524 | { | ||
525 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
526 | GNUNET_GETOPT_option_string ('f', | ||
527 | "filename", | ||
528 | "NAME", | ||
529 | gettext_noop ( | ||
530 | "use NAME for the name of the revocation file"), | ||
531 | &filename), | ||
532 | |||
533 | GNUNET_GETOPT_option_string ( | ||
534 | 'R', | ||
535 | "revoke", | ||
536 | "NAME", | ||
537 | gettext_noop ( | ||
538 | "revoke the private key associated for the the private key associated with the ego NAME "), | ||
539 | &revoke_ego), | ||
540 | |||
541 | GNUNET_GETOPT_option_flag ( | ||
542 | 'p', | ||
543 | "perform", | ||
544 | gettext_noop ( | ||
545 | "actually perform revocation, otherwise we just do the precomputation"), | ||
546 | &perform), | ||
547 | |||
548 | GNUNET_GETOPT_option_string ('t', | ||
549 | "test", | ||
550 | "KEY", | ||
551 | gettext_noop ( | ||
552 | "test if the public key KEY has been revoked"), | ||
553 | &test_ego), | ||
554 | GNUNET_GETOPT_option_uint ('e', | ||
555 | "epochs", | ||
556 | "EPOCHS", | ||
557 | gettext_noop ( | ||
558 | "number of epochs to calculate for"), | ||
559 | &epochs), | ||
560 | |||
561 | GNUNET_GETOPT_OPTION_END | ||
562 | }; | ||
563 | |||
564 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
565 | return 2; | ||
566 | |||
567 | ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, | ||
568 | argv, | ||
569 | "gnunet-revocation", | ||
570 | gettext_noop ("help text"), | ||
571 | options, | ||
572 | &run, | ||
573 | NULL)) | ||
574 | ? ret | ||
575 | : 1; | ||
576 | GNUNET_free_nz ((void *) argv); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | |||
581 | /* end of gnunet-revocation.c */ | ||
diff --git a/src/cli/revocation/meson.build b/src/cli/revocation/meson.build new file mode 100644 index 000000000..090b381df --- /dev/null +++ b/src/cli/revocation/meson.build | |||
@@ -0,0 +1,22 @@ | |||
1 | executable ('gnunet-revocation', | ||
2 | ['gnunet-revocation.c'], | ||
3 | dependencies: [libgnunetrevocation_dep, | ||
4 | libgnunetutil_dep, | ||
5 | libgnunetstatistics_dep, | ||
6 | libgnunetcore_dep, | ||
7 | libgnunetgnsrecord_dep, | ||
8 | libgnunetsetu_dep, | ||
9 | libgnunetidentity_dep], | ||
10 | include_directories: [incdir, configuration_inc], | ||
11 | install: true, | ||
12 | install_dir: get_option('bindir')) | ||
13 | |||
14 | executable ('gnunet-revocation-tvg', | ||
15 | ['gnunet-revocation.c'], | ||
16 | dependencies: [libgnunetrevocation_dep, | ||
17 | libgnunetutil_dep, | ||
18 | libgnunetgnsrecord_dep, | ||
19 | libgnunetidentity_dep], | ||
20 | include_directories: [incdir, configuration_inc], | ||
21 | install: false) | ||
22 | |||
diff --git a/src/cli/revocation/test_local_revocation.py.in b/src/cli/revocation/test_local_revocation.py.in new file mode 100644 index 000000000..e667c10ce --- /dev/null +++ b/src/cli/revocation/test_local_revocation.py.in | |||
@@ -0,0 +1,129 @@ | |||
1 | #!@PYTHONEXE@ | ||
2 | # This file is part of GNUnet. | ||
3 | # (C) 2010, 2018 Christian Grothoff (and other contributing authors) | ||
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 | # Testcase for ego revocation | ||
21 | |||
22 | import sys | ||
23 | import os | ||
24 | import subprocess | ||
25 | import re | ||
26 | import shutil | ||
27 | |||
28 | if os.name == 'posix': | ||
29 | config = 'gnunet-config' | ||
30 | gnunetarm = 'gnunet-arm' | ||
31 | ident = 'gnunet-identity' | ||
32 | revoc = './gnunet-revocation' | ||
33 | elif os.name == 'nt': | ||
34 | config = 'gnunet-config.exe' | ||
35 | gnunetarm = 'gnunet-arm.exe' | ||
36 | ident = 'gnunet-identity.exe' | ||
37 | revoc = './gnunet-revocation.exe' | ||
38 | |||
39 | TEST_CONFIGURATION = "test_revocation.conf" | ||
40 | TEST_REVOCATION_EGO = "revoc_test" | ||
41 | |||
42 | get_clean = subprocess.Popen([ | ||
43 | config, '-c', TEST_CONFIGURATION, '-s', 'PATHS', '-o', 'GNUNET_HOME', '-f' | ||
44 | ], | ||
45 | stdout=subprocess.PIPE) | ||
46 | cleandir, x = get_clean.communicate() | ||
47 | cleandir = cleandir.decode("utf-8") | ||
48 | cleandir = cleandir.rstrip('\n').rstrip('\r') | ||
49 | |||
50 | if os.path.isdir(cleandir): | ||
51 | shutil.rmtree(cleandir, True) | ||
52 | |||
53 | res = 0 | ||
54 | arm = subprocess.Popen([gnunetarm, '-s', '-c', TEST_CONFIGURATION]) | ||
55 | arm.communicate() | ||
56 | |||
57 | try: | ||
58 | print("Creating an ego " + TEST_REVOCATION_EGO) | ||
59 | sys.stdout.flush() | ||
60 | sys.stderr.flush() | ||
61 | idc = subprocess.Popen([ | ||
62 | ident, '-C', TEST_REVOCATION_EGO, '-c', TEST_CONFIGURATION | ||
63 | ]) | ||
64 | idc.communicate() | ||
65 | if idc.returncode != 0: | ||
66 | raise Exception( | ||
67 | "gnunet-identity failed to create an ego `" + TEST_REVOCATION_EGO + | ||
68 | "'" | ||
69 | ) | ||
70 | |||
71 | sys.stdout.flush() | ||
72 | sys.stderr.flush() | ||
73 | idd = subprocess.Popen([ident, '-d'], stdout=subprocess.PIPE) | ||
74 | rev_key, x = idd.communicate() | ||
75 | rev_key = rev_key.decode("utf-8") | ||
76 | if len(rev_key.split()) < 3: | ||
77 | raise Exception("can't get revocation key out of `" + rev_key + "'") | ||
78 | rev_key = rev_key.split()[2] | ||
79 | |||
80 | print("Testing key " + rev_key) | ||
81 | sys.stdout.flush() | ||
82 | sys.stderr.flush() | ||
83 | tst = subprocess.Popen([revoc, '-t', rev_key, '-c', TEST_CONFIGURATION], | ||
84 | stdout=subprocess.PIPE) | ||
85 | output_not_revoked, x = tst.communicate() | ||
86 | output_not_revoked = output_not_revoked.decode("utf-8") | ||
87 | if tst.returncode != 0: | ||
88 | raise Exception( | ||
89 | "gnunet-revocation failed to test a key - " + str(tst.returncode) + | ||
90 | ": " + output_not_revoked | ||
91 | ) | ||
92 | if 'valid' not in output_not_revoked: | ||
93 | res = 1 | ||
94 | print("Key was not valid") | ||
95 | else: | ||
96 | print("Key was valid") | ||
97 | |||
98 | print("Revoking key " + rev_key) | ||
99 | sys.stdout.flush() | ||
100 | sys.stderr.flush() | ||
101 | rev = subprocess.Popen([ | ||
102 | revoc, '-R', TEST_REVOCATION_EGO, '-p', '-c', TEST_CONFIGURATION | ||
103 | ]) | ||
104 | rev.communicate() | ||
105 | if rev.returncode != 0: | ||
106 | raise Exception("gnunet-revocation failed to revoke a key") | ||
107 | |||
108 | print("Testing revoked key " + rev_key) | ||
109 | sys.stdout.flush() | ||
110 | sys.stderr.flush() | ||
111 | tst = subprocess.Popen([revoc, '-t', rev_key, '-c', TEST_CONFIGURATION], | ||
112 | stdout=subprocess.PIPE) | ||
113 | output_revoked, x = tst.communicate() | ||
114 | output_revoked = output_revoked.decode("utf-8") | ||
115 | if tst.returncode != 0: | ||
116 | raise Exception("gnunet-revocation failed to test a revoked key") | ||
117 | if 'revoked' not in output_revoked: | ||
118 | res = 1 | ||
119 | print("Key was not revoked") | ||
120 | else: | ||
121 | print("Key was revoked") | ||
122 | |||
123 | finally: | ||
124 | arm = subprocess.Popen([gnunetarm, '-e', '-c', TEST_CONFIGURATION]) | ||
125 | arm.communicate() | ||
126 | if os.path.isdir(cleandir): | ||
127 | shutil.rmtree(cleandir, True) | ||
128 | |||
129 | sys.exit(res) | ||
diff --git a/src/cli/statistics/.gitignore b/src/cli/statistics/.gitignore new file mode 100644 index 000000000..2a7218e76 --- /dev/null +++ b/src/cli/statistics/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-statistics | |||
diff --git a/src/cli/statistics/Makefile.am b/src/cli/statistics/Makefile.am new file mode 100644 index 000000000..29160ed71 --- /dev/null +++ b/src/cli/statistics/Makefile.am | |||
@@ -0,0 +1,40 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | bin_PROGRAMS = \ | ||
14 | gnunet-statistics | ||
15 | |||
16 | gnunet_statistics_SOURCES = \ | ||
17 | gnunet-statistics.c | ||
18 | gnunet_statistics_LDADD = \ | ||
19 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
20 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
21 | $(GN_LIBINTL) | ||
22 | |||
23 | # Config file still in service folder | ||
24 | #if HAVE_PYTHON | ||
25 | #check_SCRIPTS = \ | ||
26 | # test_gnunet_statistics.py | ||
27 | #endif | ||
28 | # | ||
29 | #SUFFIXES = .py.in .py | ||
30 | #.py.in.py: | ||
31 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/$< > $@ | ||
32 | # chmod +x $@ | ||
33 | # | ||
34 | #test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile | ||
35 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py | ||
36 | # chmod +x test_gnunet_statistics.py | ||
37 | # | ||
38 | #EXTRA_DIST = \ | ||
39 | # test_statistics_api_data.conf \ | ||
40 | # test_gnunet_statistics.py.in | ||
diff --git a/src/cli/statistics/gnunet-statistics.c b/src/cli/statistics/gnunet-statistics.c new file mode 100644 index 000000000..3336980d1 --- /dev/null +++ b/src/cli/statistics/gnunet-statistics.c | |||
@@ -0,0 +1,889 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001, 2002, 2004-2007, 2009, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file statistics/gnunet-statistics.c | ||
23 | * @brief tool to obtain statistics | ||
24 | * @author Christian Grothoff | ||
25 | * @author Igor Wronsky | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_statistics_service.h" | ||
30 | #include "../../service/statistics/statistics.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Final status code. | ||
35 | */ | ||
36 | static int ret; | ||
37 | |||
38 | /** | ||
39 | * Set to subsystem that we're going to get stats for (or NULL for all). | ||
40 | */ | ||
41 | static char *subsystem; | ||
42 | |||
43 | /** | ||
44 | * The path of the testbed data. | ||
45 | */ | ||
46 | static char *path_testbed; | ||
47 | |||
48 | /** | ||
49 | * Set to the specific stat value that we are after (or NULL for all). | ||
50 | */ | ||
51 | static char *name; | ||
52 | |||
53 | /** | ||
54 | * Make the value that is being set persistent. | ||
55 | */ | ||
56 | static int persistent; | ||
57 | |||
58 | /** | ||
59 | * Watch value continuously | ||
60 | */ | ||
61 | static int watch; | ||
62 | |||
63 | /** | ||
64 | * Quiet mode | ||
65 | */ | ||
66 | static int quiet; | ||
67 | |||
68 | /** | ||
69 | * @brief Separator string for csv. | ||
70 | */ | ||
71 | static char *csv_separator; | ||
72 | |||
73 | /** | ||
74 | * Remote host | ||
75 | */ | ||
76 | static char *remote_host; | ||
77 | |||
78 | /** | ||
79 | * Remote host's port | ||
80 | */ | ||
81 | static unsigned long long remote_port; | ||
82 | |||
83 | /** | ||
84 | * Value to set | ||
85 | */ | ||
86 | static unsigned long long set_val; | ||
87 | |||
88 | /** | ||
89 | * Set operation | ||
90 | */ | ||
91 | static int set_value; | ||
92 | |||
93 | /** | ||
94 | * @brief Representation of all (testbed) nodes. | ||
95 | */ | ||
96 | static struct Node | ||
97 | { | ||
98 | /** | ||
99 | * @brief Index of the node in this array. | ||
100 | */ | ||
101 | unsigned index_node; | ||
102 | |||
103 | /** | ||
104 | * @brief Configuration handle for this node | ||
105 | */ | ||
106 | struct GNUNET_CONFIGURATION_Handle *conf; | ||
107 | |||
108 | /** | ||
109 | * Handle for pending GET operation. | ||
110 | */ | ||
111 | struct GNUNET_STATISTICS_GetHandle *gh; | ||
112 | |||
113 | /** | ||
114 | * @brief Statistics handle nodes. | ||
115 | */ | ||
116 | struct GNUNET_STATISTICS_Handle *handle; | ||
117 | /** | ||
118 | * @brief Identifier for shutdown task for this node. | ||
119 | */ | ||
120 | struct GNUNET_SCHEDULER_Task *shutdown_task; | ||
121 | } *nodes; | ||
122 | |||
123 | /** | ||
124 | * @brief Number of configurations of all (testbed) nodes. | ||
125 | */ | ||
126 | static unsigned num_nodes; | ||
127 | |||
128 | /** | ||
129 | * @brief Set of values for a combination of subsystem and name. | ||
130 | */ | ||
131 | struct ValueSet | ||
132 | { | ||
133 | /** | ||
134 | * @brief Subsystem of the valueset. | ||
135 | */ | ||
136 | char *subsystem; | ||
137 | |||
138 | /** | ||
139 | * @brief Name of the valueset. | ||
140 | */ | ||
141 | char *name; | ||
142 | |||
143 | /** | ||
144 | * @brief The values. | ||
145 | */ | ||
146 | uint64_t *values; | ||
147 | |||
148 | /** | ||
149 | * @brief Persistence of the values. | ||
150 | */ | ||
151 | int is_persistent; | ||
152 | }; | ||
153 | |||
154 | |||
155 | /** | ||
156 | * @brief Collection of all values (represented with #ValueSet). | ||
157 | */ | ||
158 | static struct GNUNET_CONTAINER_MultiHashMap *values; | ||
159 | |||
160 | /** | ||
161 | * @brief Number of nodes that have their values ready. | ||
162 | */ | ||
163 | static int num_nodes_ready; | ||
164 | |||
165 | /** | ||
166 | * @brief Number of nodes that have their values ready. | ||
167 | */ | ||
168 | static int num_nodes_ready_shutdown; | ||
169 | |||
170 | |||
171 | /** | ||
172 | * @brief Create a new #ValueSet | ||
173 | * | ||
174 | * @param subsystem Subsystem of the valueset. | ||
175 | * @param name Name of the valueset. | ||
176 | * @param num_values Number of values in valueset - number of peers. | ||
177 | * @param is_persistent Persistence status of values. | ||
178 | * @return Newly allocated #ValueSet. | ||
179 | */ | ||
180 | static struct ValueSet * | ||
181 | new_value_set (const char *subsystem, | ||
182 | const char *name, | ||
183 | unsigned num_values, | ||
184 | int is_persistent) | ||
185 | { | ||
186 | struct ValueSet *value_set; | ||
187 | |||
188 | value_set = GNUNET_new (struct ValueSet); | ||
189 | value_set->subsystem = GNUNET_strdup (subsystem); | ||
190 | value_set->name = GNUNET_strdup (name); | ||
191 | value_set->values = GNUNET_new_array (num_values, | ||
192 | uint64_t); | ||
193 | value_set->is_persistent = persistent; | ||
194 | return value_set; | ||
195 | } | ||
196 | |||
197 | |||
198 | /** | ||
199 | * @brief Print the (collected) values. | ||
200 | * | ||
201 | * Implements #GNUNET_CONTAINER_HashMapIterator. | ||
202 | * | ||
203 | * @param cls Closure - unused | ||
204 | * @param key #GNUNET_HashCode key of #GNUNET_CONTAINER_MultiHashMap iterator - | ||
205 | * unused | ||
206 | * @param value Values represented as #ValueSet. | ||
207 | * @return #GNUNET_YES - continue iteration. | ||
208 | */ | ||
209 | static int | ||
210 | printer (void *cls, | ||
211 | const struct GNUNET_HashCode *key, | ||
212 | void *value) | ||
213 | { | ||
214 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
215 | const char *now_str; | ||
216 | struct ValueSet *value_set = value; | ||
217 | |||
218 | if (quiet == GNUNET_NO) | ||
219 | { | ||
220 | if (GNUNET_YES == watch) | ||
221 | { | ||
222 | now_str = GNUNET_STRINGS_absolute_time_to_string (now); | ||
223 | fprintf (stdout, | ||
224 | "%24s%s %s%s%12s%s %s%50s%s%s ", | ||
225 | now_str, | ||
226 | csv_separator, | ||
227 | value_set->is_persistent ? "!" : " ", | ||
228 | csv_separator, | ||
229 | value_set->subsystem, | ||
230 | csv_separator, | ||
231 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
232 | _ (value_set->name), | ||
233 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
234 | (0 == strlen (csv_separator) ? ":" : csv_separator)); | ||
235 | } | ||
236 | else | ||
237 | { | ||
238 | fprintf (stdout, | ||
239 | "%s%s%12s%s %s%50s%s%s ", | ||
240 | value_set->is_persistent ? "!" : " ", | ||
241 | csv_separator, | ||
242 | value_set->subsystem, | ||
243 | csv_separator, | ||
244 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
245 | _ (value_set->name), | ||
246 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
247 | (0 == strlen (csv_separator) ? ":" : csv_separator)); | ||
248 | } | ||
249 | } | ||
250 | for (unsigned i = 0; i < num_nodes; i++) | ||
251 | { | ||
252 | fprintf (stdout, | ||
253 | "%16llu%s", | ||
254 | (unsigned long long) value_set->values[i], | ||
255 | csv_separator); | ||
256 | } | ||
257 | fprintf (stdout, "\n"); | ||
258 | GNUNET_free (value_set->subsystem); | ||
259 | GNUNET_free (value_set->name); | ||
260 | GNUNET_free (value_set->values); | ||
261 | GNUNET_free (value_set); | ||
262 | return GNUNET_YES; | ||
263 | } | ||
264 | |||
265 | |||
266 | /** | ||
267 | * Callback function to process statistic values. | ||
268 | * | ||
269 | * @param cls closure | ||
270 | * @param subsystem name of subsystem that created the statistic | ||
271 | * @param name the name of the datum | ||
272 | * @param value the current value | ||
273 | * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not | ||
274 | * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration | ||
275 | */ | ||
276 | static int | ||
277 | printer_watch (void *cls, | ||
278 | const char *subsystem, | ||
279 | const char *name, | ||
280 | uint64_t value, | ||
281 | int is_persistent) | ||
282 | { | ||
283 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
284 | const char *now_str; | ||
285 | |||
286 | if (quiet == GNUNET_NO) | ||
287 | { | ||
288 | if (GNUNET_YES == watch) | ||
289 | { | ||
290 | now_str = GNUNET_STRINGS_absolute_time_to_string (now); | ||
291 | fprintf (stdout, | ||
292 | "%24s%s %s%s%12s%s %s%50s%s%s %16llu\n", | ||
293 | now_str, | ||
294 | csv_separator, | ||
295 | is_persistent ? "!" : " ", | ||
296 | csv_separator, | ||
297 | subsystem, | ||
298 | csv_separator, | ||
299 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
300 | _ (name), | ||
301 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
302 | (0 == strlen (csv_separator) ? ":" : csv_separator), | ||
303 | (unsigned long long) value); | ||
304 | } | ||
305 | else | ||
306 | { | ||
307 | fprintf (stdout, | ||
308 | "%s%s%12s%s %s%50s%s%s %16llu\n", | ||
309 | is_persistent ? "!" : " ", | ||
310 | csv_separator, | ||
311 | subsystem, | ||
312 | csv_separator, | ||
313 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
314 | _ (name), | ||
315 | (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ | ||
316 | (0 == strlen (csv_separator) ? ":" : csv_separator), | ||
317 | (unsigned long long) value); | ||
318 | } | ||
319 | } | ||
320 | else | ||
321 | fprintf (stdout, "%llu\n", (unsigned long long) value); | ||
322 | |||
323 | return GNUNET_OK; | ||
324 | } | ||
325 | |||
326 | |||
327 | /** | ||
328 | * @brief Clean all data structures related to given node. | ||
329 | * | ||
330 | * Also clears global structures if we are the last node to clean. | ||
331 | * | ||
332 | * @param cls the index of the node | ||
333 | */ | ||
334 | static void | ||
335 | clean_node (void *cls) | ||
336 | { | ||
337 | const unsigned index_node = *(unsigned *) cls; | ||
338 | struct GNUNET_STATISTICS_Handle *h; | ||
339 | struct GNUNET_STATISTICS_GetHandle *gh; | ||
340 | |||
341 | if ((NULL != path_testbed) && /* were issued with -t <testbed-path> option */ | ||
342 | (NULL != nodes[index_node].conf)) | ||
343 | { | ||
344 | GNUNET_CONFIGURATION_destroy (nodes[index_node].conf); | ||
345 | nodes[index_node].conf = NULL; | ||
346 | } | ||
347 | |||
348 | h = nodes[index_node].handle; | ||
349 | gh = nodes[index_node].gh; | ||
350 | |||
351 | if (NULL != gh) | ||
352 | { | ||
353 | GNUNET_STATISTICS_get_cancel (gh); | ||
354 | gh = NULL; | ||
355 | } | ||
356 | if (GNUNET_YES == watch) | ||
357 | { | ||
358 | GNUNET_assert ( | ||
359 | GNUNET_OK == | ||
360 | GNUNET_STATISTICS_watch_cancel (h, | ||
361 | subsystem, | ||
362 | name, | ||
363 | &printer_watch, | ||
364 | &nodes[index_node].index_node)); | ||
365 | } | ||
366 | |||
367 | if (NULL != h) | ||
368 | { | ||
369 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
370 | h = NULL; | ||
371 | } | ||
372 | |||
373 | num_nodes_ready_shutdown++; | ||
374 | } | ||
375 | |||
376 | |||
377 | /** | ||
378 | * @brief Print and shutdown | ||
379 | * | ||
380 | * @param cls unused | ||
381 | */ | ||
382 | static void | ||
383 | print_finish (void *cls) | ||
384 | { | ||
385 | GNUNET_CONTAINER_multihashmap_iterate (values, | ||
386 | &printer, | ||
387 | NULL); | ||
388 | GNUNET_CONTAINER_multihashmap_destroy (values); | ||
389 | GNUNET_SCHEDULER_shutdown (); | ||
390 | } | ||
391 | |||
392 | |||
393 | /** | ||
394 | * @brief Called once all statistic values are available. | ||
395 | * | ||
396 | * Implements #GNUNET_STATISTICS_Callback | ||
397 | * | ||
398 | * @param cls Closure - The index of the node. | ||
399 | * @param success Whether statistics were obtained successfully. | ||
400 | */ | ||
401 | static void | ||
402 | continuation_print (void *cls, | ||
403 | int success) | ||
404 | { | ||
405 | const unsigned index_node = *(unsigned *) cls; | ||
406 | |||
407 | nodes[index_node].gh = NULL; | ||
408 | if (GNUNET_OK != success) | ||
409 | { | ||
410 | if (NULL == remote_host) | ||
411 | fprintf (stderr, | ||
412 | "%s", | ||
413 | _ ("Failed to obtain statistics.\n")); | ||
414 | else | ||
415 | fprintf (stderr, | ||
416 | _ ("Failed to obtain statistics from host `%s:%llu'\n"), | ||
417 | remote_host, | ||
418 | remote_port); | ||
419 | ret = 1; | ||
420 | } | ||
421 | if (NULL != nodes[index_node].shutdown_task) | ||
422 | { | ||
423 | GNUNET_SCHEDULER_cancel (nodes[index_node].shutdown_task); | ||
424 | nodes[index_node].shutdown_task = NULL; | ||
425 | } | ||
426 | GNUNET_SCHEDULER_add_now (&clean_node, | ||
427 | &nodes[index_node].index_node); | ||
428 | num_nodes_ready++; | ||
429 | if (num_nodes_ready == num_nodes) | ||
430 | { | ||
431 | GNUNET_SCHEDULER_add_now (&print_finish, | ||
432 | NULL); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | |||
437 | /** | ||
438 | * Function called last by the statistics code. | ||
439 | * | ||
440 | * @param cls closure | ||
441 | * @param success #GNUNET_OK if statistics were | ||
442 | * successfully obtained, #GNUNET_SYSERR if not. | ||
443 | */ | ||
444 | static void | ||
445 | cleanup (void *cls, | ||
446 | int success) | ||
447 | { | ||
448 | for (unsigned i = 0; i < num_nodes; i++) | ||
449 | { | ||
450 | nodes[i].gh = NULL; | ||
451 | } | ||
452 | if (GNUNET_OK != success) | ||
453 | { | ||
454 | if (NULL == remote_host) | ||
455 | fprintf (stderr, "%s", _ ("Failed to obtain statistics.\n")); | ||
456 | else | ||
457 | fprintf (stderr, | ||
458 | _ ("Failed to obtain statistics from host `%s:%llu'\n"), | ||
459 | remote_host, | ||
460 | remote_port); | ||
461 | ret = 1; | ||
462 | } | ||
463 | GNUNET_SCHEDULER_shutdown (); | ||
464 | } | ||
465 | |||
466 | |||
467 | /** | ||
468 | * @brief Iterate over statistics values and store them in #values. | ||
469 | * They will be printed once all are available. | ||
470 | * | ||
471 | * @param cls Cosure - Node index. | ||
472 | * @param subsystem Subsystem of the value. | ||
473 | * @param name Name of the value. | ||
474 | * @param value Value itself. | ||
475 | * @param is_persistent Persistence. | ||
476 | * @return #GNUNET_OK - continue. | ||
477 | */ | ||
478 | static int | ||
479 | collector (void *cls, | ||
480 | const char *subsystem, | ||
481 | const char *name, | ||
482 | uint64_t value, | ||
483 | int is_persistent) | ||
484 | { | ||
485 | const unsigned index_node = *(unsigned *) cls; | ||
486 | struct GNUNET_HashCode *key; | ||
487 | struct GNUNET_HashCode hc; | ||
488 | char *subsys_name; | ||
489 | unsigned len_subsys_name; | ||
490 | struct ValueSet *value_set; | ||
491 | |||
492 | len_subsys_name = strlen (subsystem) + 3 + strlen (name) + 1; | ||
493 | subsys_name = GNUNET_malloc (len_subsys_name); | ||
494 | sprintf (subsys_name, "%s---%s", subsystem, name); | ||
495 | key = &hc; | ||
496 | GNUNET_CRYPTO_hash (subsys_name, len_subsys_name, key); | ||
497 | GNUNET_free (subsys_name); | ||
498 | if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (values, key)) | ||
499 | { | ||
500 | value_set = GNUNET_CONTAINER_multihashmap_get (values, key); | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | value_set = new_value_set (subsystem, name, num_nodes, is_persistent); | ||
505 | } | ||
506 | value_set->values[index_node] = value; | ||
507 | GNUNET_assert (GNUNET_YES == | ||
508 | GNUNET_CONTAINER_multihashmap_put ( | ||
509 | values, | ||
510 | key, | ||
511 | value_set, | ||
512 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
513 | return GNUNET_OK; | ||
514 | } | ||
515 | |||
516 | |||
517 | /** | ||
518 | * Main task that does the actual work. | ||
519 | * | ||
520 | * @param cls closure with our configuration | ||
521 | */ | ||
522 | static void | ||
523 | main_task (void *cls) | ||
524 | { | ||
525 | unsigned index_node = *(unsigned *) cls; | ||
526 | const struct GNUNET_CONFIGURATION_Handle *cfg = nodes[index_node].conf; | ||
527 | |||
528 | if (set_value) | ||
529 | { | ||
530 | if (NULL == subsystem) | ||
531 | { | ||
532 | fprintf (stderr, "%s", _ ("Missing argument: subsystem \n")); | ||
533 | ret = 1; | ||
534 | return; | ||
535 | } | ||
536 | if (NULL == name) | ||
537 | { | ||
538 | fprintf (stderr, "%s", _ ("Missing argument: name\n")); | ||
539 | ret = 1; | ||
540 | return; | ||
541 | } | ||
542 | nodes[index_node].handle = GNUNET_STATISTICS_create (subsystem, cfg); | ||
543 | if (NULL == nodes[index_node].handle) | ||
544 | { | ||
545 | ret = 1; | ||
546 | return; | ||
547 | } | ||
548 | GNUNET_STATISTICS_set (nodes[index_node].handle, | ||
549 | name, | ||
550 | (uint64_t) set_val, | ||
551 | persistent); | ||
552 | GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_YES); | ||
553 | nodes[index_node].handle = NULL; | ||
554 | return; | ||
555 | } | ||
556 | if (NULL == (nodes[index_node].handle = | ||
557 | GNUNET_STATISTICS_create ("gnunet-statistics", cfg))) | ||
558 | { | ||
559 | ret = 1; | ||
560 | return; | ||
561 | } | ||
562 | if (GNUNET_NO == watch) | ||
563 | { | ||
564 | if (NULL == (nodes[index_node].gh = | ||
565 | GNUNET_STATISTICS_get (nodes[index_node].handle, | ||
566 | subsystem, | ||
567 | name, | ||
568 | &continuation_print, | ||
569 | &collector, | ||
570 | &nodes[index_node].index_node))) | ||
571 | cleanup (nodes[index_node].handle, GNUNET_SYSERR); | ||
572 | } | ||
573 | else | ||
574 | { | ||
575 | if ((NULL == subsystem) || (NULL == name)) | ||
576 | { | ||
577 | printf (_ ("No subsystem or name given\n")); | ||
578 | GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_NO); | ||
579 | nodes[index_node].handle = NULL; | ||
580 | ret = 1; | ||
581 | return; | ||
582 | } | ||
583 | if (GNUNET_OK != GNUNET_STATISTICS_watch (nodes[index_node].handle, | ||
584 | subsystem, | ||
585 | name, | ||
586 | &printer_watch, | ||
587 | &nodes[index_node].index_node)) | ||
588 | { | ||
589 | fprintf (stderr, _ ("Failed to initialize watch routine\n")); | ||
590 | nodes[index_node].shutdown_task = | ||
591 | GNUNET_SCHEDULER_add_now (&clean_node, &nodes[index_node].index_node); | ||
592 | return; | ||
593 | } | ||
594 | } | ||
595 | nodes[index_node].shutdown_task = | ||
596 | GNUNET_SCHEDULER_add_shutdown (&clean_node, &nodes[index_node].index_node); | ||
597 | } | ||
598 | |||
599 | |||
600 | /** | ||
601 | * @brief Iter over content of a node's directory to check for existence of a | ||
602 | * config file. | ||
603 | * | ||
604 | * Implements #GNUNET_FileNameCallback | ||
605 | * | ||
606 | * @param cls pointer to indicate success | ||
607 | * @param filename filename inside the directory of the potential node | ||
608 | * | ||
609 | * @return to continue iteration or not to | ||
610 | */ | ||
611 | static int | ||
612 | iter_check_config (void *cls, | ||
613 | const char *filename) | ||
614 | { | ||
615 | if (0 == strncmp (GNUNET_STRINGS_get_short_name (filename), "config", 6)) | ||
616 | { | ||
617 | /* Found the config - stop iteration successfully */ | ||
618 | GNUNET_array_grow (nodes, num_nodes, num_nodes + 1); | ||
619 | nodes[num_nodes - 1].conf = GNUNET_CONFIGURATION_create (); | ||
620 | nodes[num_nodes - 1].index_node = num_nodes - 1; | ||
621 | if (GNUNET_OK != | ||
622 | GNUNET_CONFIGURATION_load (nodes[num_nodes - 1].conf, filename)) | ||
623 | { | ||
624 | fprintf (stderr, "Failed loading config `%s'\n", filename); | ||
625 | return GNUNET_SYSERR; | ||
626 | } | ||
627 | return GNUNET_NO; | ||
628 | } | ||
629 | else | ||
630 | { | ||
631 | /* Continue iteration */ | ||
632 | return GNUNET_OK; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | |||
637 | /** | ||
638 | * @brief Iterates over filenames in testbed directory. | ||
639 | * | ||
640 | * Implements #GNUNET_FileNameCallback | ||
641 | * | ||
642 | * Checks if the file is a directory for a testbed node | ||
643 | * and counts the nodes. | ||
644 | * | ||
645 | * @param cls counter of nodes | ||
646 | * @param filename full path of the file in testbed | ||
647 | * @return status whether to continue iteration | ||
648 | */ | ||
649 | static int | ||
650 | iter_testbed_path (void *cls, | ||
651 | const char *filename) | ||
652 | { | ||
653 | unsigned index_node; | ||
654 | |||
655 | GNUNET_assert (NULL != filename); | ||
656 | if (1 == sscanf (GNUNET_STRINGS_get_short_name (filename), | ||
657 | "%u", | ||
658 | &index_node)) | ||
659 | { | ||
660 | if (-1 == GNUNET_DISK_directory_scan (filename, | ||
661 | iter_check_config, | ||
662 | NULL)) | ||
663 | { | ||
664 | /* This is probably no directory for a testbed node | ||
665 | * Go on with iteration */ | ||
666 | return GNUNET_OK; | ||
667 | } | ||
668 | return GNUNET_OK; | ||
669 | } | ||
670 | return GNUNET_OK; | ||
671 | } | ||
672 | |||
673 | |||
674 | /** | ||
675 | * @brief Count the number of nodes running in the testbed | ||
676 | * | ||
677 | * @param path_testbed path to the testbed data | ||
678 | * | ||
679 | * @return number of running nodes | ||
680 | */ | ||
681 | static int | ||
682 | discover_testbed_nodes (const char *path_testbed) | ||
683 | { | ||
684 | int num_dir_entries; | ||
685 | |||
686 | num_dir_entries = | ||
687 | GNUNET_DISK_directory_scan (path_testbed, | ||
688 | &iter_testbed_path, | ||
689 | NULL); | ||
690 | if (-1 == num_dir_entries) | ||
691 | { | ||
692 | fprintf (stderr, | ||
693 | "Failure during scanning directory `%s'\n", | ||
694 | path_testbed); | ||
695 | return -1; | ||
696 | } | ||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | |||
701 | /** | ||
702 | * Main function that will be run by the scheduler. | ||
703 | * | ||
704 | * @param cls closure | ||
705 | * @param args remaining command-line arguments | ||
706 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
707 | * @param cfg configuration | ||
708 | */ | ||
709 | static void | ||
710 | run (void *cls, | ||
711 | char *const *args, | ||
712 | const char *cfgfile, | ||
713 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
714 | { | ||
715 | struct GNUNET_CONFIGURATION_Handle *c; | ||
716 | |||
717 | c = (struct GNUNET_CONFIGURATION_Handle *) cfg; | ||
718 | set_value = GNUNET_NO; | ||
719 | if (NULL == csv_separator) | ||
720 | csv_separator = ""; | ||
721 | if (NULL != args[0]) | ||
722 | { | ||
723 | if (1 != sscanf (args[0], "%llu", &set_val)) | ||
724 | { | ||
725 | fprintf (stderr, _ ("Invalid argument `%s'\n"), args[0]); | ||
726 | ret = 1; | ||
727 | return; | ||
728 | } | ||
729 | set_value = GNUNET_YES; | ||
730 | } | ||
731 | if (NULL != remote_host) | ||
732 | { | ||
733 | if (0 == remote_port) | ||
734 | { | ||
735 | if (GNUNET_SYSERR == | ||
736 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
737 | "statistics", | ||
738 | "PORT", | ||
739 | &remote_port)) | ||
740 | { | ||
741 | fprintf (stderr, | ||
742 | _ ("A port is required to connect to host `%s'\n"), | ||
743 | remote_host); | ||
744 | return; | ||
745 | } | ||
746 | } | ||
747 | else if (65535 <= remote_port) | ||
748 | { | ||
749 | fprintf (stderr, | ||
750 | _ ( | ||
751 | "A port has to be between 1 and 65535 to connect to host `%s'\n"), | ||
752 | remote_host); | ||
753 | return; | ||
754 | } | ||
755 | |||
756 | /* Manipulate configuration */ | ||
757 | GNUNET_CONFIGURATION_set_value_string (c, | ||
758 | "statistics", | ||
759 | "UNIXPATH", | ||
760 | ""); | ||
761 | GNUNET_CONFIGURATION_set_value_string (c, | ||
762 | "statistics", | ||
763 | "HOSTNAME", | ||
764 | remote_host); | ||
765 | GNUNET_CONFIGURATION_set_value_number (c, | ||
766 | "statistics", | ||
767 | "PORT", | ||
768 | remote_port); | ||
769 | } | ||
770 | if (NULL == path_testbed) | ||
771 | { | ||
772 | values = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
773 | GNUNET_array_grow (nodes, num_nodes, 1); | ||
774 | nodes[0].index_node = 0; | ||
775 | nodes[0].conf = c; | ||
776 | GNUNET_SCHEDULER_add_now (&main_task, &nodes[0].index_node); | ||
777 | } | ||
778 | else | ||
779 | { | ||
780 | if (GNUNET_YES == watch) | ||
781 | { | ||
782 | printf ( | ||
783 | _ ("Not able to watch testbed nodes (yet - feel free to implement)\n")); | ||
784 | ret = 1; | ||
785 | return; | ||
786 | } | ||
787 | values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); | ||
788 | if (-1 == discover_testbed_nodes (path_testbed)) | ||
789 | { | ||
790 | return; | ||
791 | } | ||
792 | /* For each config/node collect statistics */ | ||
793 | for (unsigned i = 0; i < num_nodes; i++) | ||
794 | { | ||
795 | GNUNET_SCHEDULER_add_now (&main_task, &nodes[i].index_node); | ||
796 | } | ||
797 | } | ||
798 | } | ||
799 | |||
800 | |||
801 | /** | ||
802 | * The main function to obtain statistics in GNUnet. | ||
803 | * | ||
804 | * @param argc number of arguments from the command line | ||
805 | * @param argv command line arguments | ||
806 | * @return 0 ok, 1 on error | ||
807 | */ | ||
808 | int | ||
809 | main (int argc, char *const *argv) | ||
810 | { | ||
811 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
812 | GNUNET_GETOPT_option_string ('n', | ||
813 | "name", | ||
814 | "NAME", | ||
815 | gettext_noop ( | ||
816 | "limit output to statistics for the given NAME"), | ||
817 | &name), | ||
818 | GNUNET_GETOPT_option_flag ('p', | ||
819 | "persistent", | ||
820 | gettext_noop ( | ||
821 | "make the value being set persistent"), | ||
822 | &persistent), | ||
823 | GNUNET_GETOPT_option_string ('s', | ||
824 | "subsystem", | ||
825 | "SUBSYSTEM", | ||
826 | gettext_noop ( | ||
827 | "limit output to the given SUBSYSTEM"), | ||
828 | &subsystem), | ||
829 | GNUNET_GETOPT_option_string ('S', | ||
830 | "csv-separator", | ||
831 | "CSV_SEPARATOR", | ||
832 | gettext_noop ("use as csv separator"), | ||
833 | &csv_separator), | ||
834 | GNUNET_GETOPT_option_filename ('t', | ||
835 | "testbed", | ||
836 | "TESTBED", | ||
837 | gettext_noop ( | ||
838 | "path to the folder containing the testbed data"), | ||
839 | &path_testbed), | ||
840 | GNUNET_GETOPT_option_flag ('q', | ||
841 | "quiet", | ||
842 | gettext_noop ( | ||
843 | "just print the statistics value"), | ||
844 | &quiet), | ||
845 | GNUNET_GETOPT_option_flag ('w', | ||
846 | "watch", | ||
847 | gettext_noop ("watch value continuously"), | ||
848 | &watch), | ||
849 | GNUNET_GETOPT_option_string ('r', | ||
850 | "remote", | ||
851 | "REMOTE", | ||
852 | gettext_noop ("connect to remote host"), | ||
853 | &remote_host), | ||
854 | GNUNET_GETOPT_option_ulong ('o', | ||
855 | "port", | ||
856 | "PORT", | ||
857 | gettext_noop ("port for remote host"), | ||
858 | &remote_port), | ||
859 | GNUNET_GETOPT_OPTION_END | ||
860 | }; | ||
861 | |||
862 | remote_port = 0; | ||
863 | remote_host = NULL; | ||
864 | if (GNUNET_OK != | ||
865 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
866 | &argc, &argv)) | ||
867 | return 2; | ||
868 | |||
869 | ret = (GNUNET_OK == | ||
870 | GNUNET_PROGRAM_run (argc, | ||
871 | argv, | ||
872 | "gnunet-statistics [options [value]]", | ||
873 | gettext_noop ( | ||
874 | "Print statistics about GNUnet operations."), | ||
875 | options, | ||
876 | &run, | ||
877 | NULL)) | ||
878 | ? ret | ||
879 | : 1; | ||
880 | GNUNET_array_grow (nodes, | ||
881 | num_nodes, | ||
882 | 0); | ||
883 | GNUNET_free (remote_host); | ||
884 | GNUNET_free_nz ((void *) argv); | ||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | |||
889 | /* end of gnunet-statistics.c */ | ||
diff --git a/src/cli/statistics/meson.build b/src/cli/statistics/meson.build new file mode 100644 index 000000000..ea0af10c7 --- /dev/null +++ b/src/cli/statistics/meson.build | |||
@@ -0,0 +1,8 @@ | |||
1 | executable ('gnunet-statistics', | ||
2 | ['gnunet-statistics.c'], | ||
3 | dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
7 | |||
8 | |||
diff --git a/src/cli/util/.gitignore b/src/cli/util/.gitignore new file mode 100644 index 000000000..9e045f16f --- /dev/null +++ b/src/cli/util/.gitignore | |||
@@ -0,0 +1,10 @@ | |||
1 | gnunet-config | ||
2 | gnunet-config-diff | ||
3 | gnunet-crypto-tvg | ||
4 | gnunet-ecc | ||
5 | gnunet-qr | ||
6 | gnunet-resolver | ||
7 | gnunet-scrypt | ||
8 | gnunet-uri | ||
9 | gnunet-base32 | ||
10 | gnunet-timeout | ||
diff --git a/src/cli/util/Makefile.am b/src/cli/util/Makefile.am new file mode 100644 index 000000000..cac477e13 --- /dev/null +++ b/src/cli/util/Makefile.am | |||
@@ -0,0 +1,108 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | libexecdir= $(pkglibdir)/libexec/ | ||
7 | |||
8 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
9 | |||
10 | if USE_COVERAGE | ||
11 | AM_CFLAGS = --coverage -O0 | ||
12 | XLIB = -lgcov | ||
13 | endif | ||
14 | |||
15 | gnunet_config_diff_SOURCES = \ | ||
16 | gnunet-config-diff.c | ||
17 | gnunet_config_diff_LDADD = \ | ||
18 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
19 | |||
20 | GNUNET_ECC = gnunet-ecc | ||
21 | GNUNET_SCRYPT = gnunet-scrypt | ||
22 | |||
23 | libexec_PROGRAMS = \ | ||
24 | gnunet-timeout | ||
25 | |||
26 | bin_PROGRAMS = \ | ||
27 | gnunet-base32 \ | ||
28 | gnunet-config \ | ||
29 | gnunet-resolver \ | ||
30 | $(GNUNET_ECC) \ | ||
31 | $(GNUNET_SCRYPT) \ | ||
32 | gnunet-uri | ||
33 | if HAVE_ZBAR | ||
34 | bin_PROGRAMS += gnunet-qr | ||
35 | endif | ||
36 | |||
37 | noinst_PROGRAMS = \ | ||
38 | gnunet-config-diff \ | ||
39 | gnunet-crypto-tvg | ||
40 | |||
41 | if ENABLE_TEST_RUN | ||
42 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
43 | TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
44 | endif | ||
45 | |||
46 | gnunet_timeout_SOURCES = \ | ||
47 | gnunet-timeout.c | ||
48 | |||
49 | gnunet_resolver_SOURCES = \ | ||
50 | gnunet-resolver.c | ||
51 | gnunet_resolver_LDADD = \ | ||
52 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
53 | $(GN_LIBINTL) | ||
54 | |||
55 | gnunet_crypto_tvg_SOURCES = \ | ||
56 | gnunet-crypto-tvg.c | ||
57 | gnunet_crypto_tvg_LDADD = \ | ||
58 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
59 | $(GN_LIBINTL) -lgcrypt -ljansson | ||
60 | |||
61 | gnunet_ecc_SOURCES = \ | ||
62 | gnunet-ecc.c | ||
63 | gnunet_ecc_LDADD = \ | ||
64 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
65 | $(GN_LIBINTL) -lgcrypt | ||
66 | |||
67 | gnunet_base32_SOURCES = \ | ||
68 | gnunet-base32.c | ||
69 | gnunet_base32_LDADD = \ | ||
70 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
71 | $(GN_LIBINTL) | ||
72 | |||
73 | gnunet_scrypt_SOURCES = \ | ||
74 | gnunet-scrypt.c | ||
75 | gnunet_scrypt_LDADD = \ | ||
76 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
77 | $(GN_LIBINTL) -lgcrypt | ||
78 | |||
79 | |||
80 | gnunet_config_SOURCES = \ | ||
81 | gnunet-config.c | ||
82 | gnunet_config_LDADD = \ | ||
83 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
84 | $(GN_LIBINTL) | ||
85 | |||
86 | gnunet_uri_SOURCES = \ | ||
87 | gnunet-uri.c | ||
88 | gnunet_uri_LDADD = \ | ||
89 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
90 | $(GN_LIBINTL) | ||
91 | |||
92 | |||
93 | gnunet_qr_SOURCES = \ | ||
94 | gnunet-qr.c | ||
95 | gnunet_qr_LDADD = \ | ||
96 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
97 | $(GN_LIBINTL) | ||
98 | gnunet_qr_LDFLAGS= -lzbar | ||
99 | if HAVE_PNG | ||
100 | gnunet_qr_LDFLAGS += -lpng | ||
101 | endif | ||
102 | |||
103 | check_SCRIPTS = \ | ||
104 | test_crypto_vectors.sh | ||
105 | |||
106 | EXTRA_DIST = \ | ||
107 | test_crypto_vectors.sh \ | ||
108 | crypto-test-vectors.json | ||
diff --git a/src/cli/util/crypto-test-vectors.json b/src/cli/util/crypto-test-vectors.json new file mode 100644 index 000000000..972b62c3e --- /dev/null +++ b/src/cli/util/crypto-test-vectors.json | |||
@@ -0,0 +1,56 @@ | |||
1 | { | ||
2 | "encoding": "base32crockford", | ||
3 | "producer": "GNUnet 0.14.0 git-64ad3b0a1", | ||
4 | "vectors": [ | ||
5 | { | ||
6 | "operation": "hash", | ||
7 | "input": "91JPRV3F5GG4EKJNDSJQ8", | ||
8 | "output": "D0R24RZ1TPASVQ2NY56CT8AJDYZE9ZGDB0GVZ05E9D4YGZQW2RC5YFPQ0Q86EPW836DY7VYQTNFFJT3ZR2K508F4JVS5JNJKYN2MMFR" | ||
9 | }, | ||
10 | { | ||
11 | "operation": "ecc_ecdh", | ||
12 | "priv1": "TFA439C75RT2JK9V6GTRRXH3QR3QC4SVJ1KBQ4MNY3S338GT6T50", | ||
13 | "pub1": "E19WJDA83485BC8EC8RV7FTAK86BESJG1YNYRENEC0JV7XEZ7M80", | ||
14 | "priv2": "2DT3B0TMY6VVP56YZKG5ASRSQAEV0GB4QMT9N6CTPDARNJ905APG", | ||
15 | "skm": "GY63DCHR6BGV2AKDM44V7A4H4DA0WJC7D5C2R7DXTWC9D83H7XM0PEQKKZ1K2HWMWBSBNPDWXDN7PA1R1WJKVQ2RDTNF1PBXCFHM9QR" | ||
16 | }, | ||
17 | { | ||
18 | "operation": "eddsa_key_derivation", | ||
19 | "priv": "8QC2VNF8443S5KPNKMB4XMV58BTHWAKZ7SVW5WG3KRB37567XS90", | ||
20 | "pub": "3M9KK1WSNM1RTY5P72HKFA264V4B7MVHVJ08Y90CV06DYHV8XPP0" | ||
21 | }, | ||
22 | { | ||
23 | "operation": "eddsa_signing", | ||
24 | "priv": "5077XJR9AMH4T97ACKFBVBJD0KFENHPV66B2Y1JBSKXBJKNZJ4E0", | ||
25 | "pub": "6E2F03JJ8AEDANTTZZ4SBZDFEEZSF8A9DVGTS6VFBCVZQYQ46RRG", | ||
26 | "data": "00000300000000000000", | ||
27 | "sig": "XCNJGJ96WPDH60YVMH6C74NGQSGJE3BC1TYMGX6BHY5DMZZZKTB373QTXJ507K5EBSG9YS2EYKHCX3ATRQ6P5MY9MXC4ZB1XSZ2X23G" | ||
28 | }, | ||
29 | { | ||
30 | "operation": "kdf", | ||
31 | "salt": "94KPT83PCNS7J83KC5P78Y8", | ||
32 | "ikm": "94KPT83MD1JJ0WV5CDS6AX10D5Q70XBM41NPAY90DNGQ8SBJD5GPR", | ||
33 | "ctx": "94KPT83141HPYVKMCNW78833D1TPWTSC41GPRWVF41NPWVVQDRG62WS04XMPWSKF4WG6JVH0EHM6A82J8S1G", | ||
34 | "out_len %u\n": 64, | ||
35 | "out": "GTMR4QT05Z9WF5HKVG0WK9RPXGHSMHJNW377G9GJXCA8B0FEKPF4D27RJMSJZYWSQNTBJ5EYVV7ZW18B48Z0JVJJ80RHB706Y96Q358" | ||
36 | }, | ||
37 | { | ||
38 | "operation": "eddsa_ecdh", | ||
39 | "priv_ecdhe": "5FBRFZY942H2PD96NFNYWZKYXCRFY11JWQ59V7G9B4M8EX1KE100", | ||
40 | "pub_ecdhe": "GDX7FC01AZYMG0BY0AMHR6E7KCGX9F6SWES16WZ1QWZ2VSYXKH00", | ||
41 | "priv_eddsa": "3HYHM9DZQ3D61APDQNBCSKJE452YEP6JK01DWR1J3VZAASFEA570", | ||
42 | "pub_eddsa": "87N7PFAHBX97HRE8XYW8KYN64YZDF4FCBR2BZ5SZN3QE3D2BF0R0", | ||
43 | "key_material": "QH0RAXXC9RYDEAXKNTAWM0WXJS25RS67H5T252EGA22RA6JTYRFDAEK8CJY85QSYWGYHQXK5Y1SWSRB3P0NXNXYP237EXMXQ3P2WE00" | ||
44 | }, | ||
45 | { | ||
46 | "operation": "rsa_blind_signing", | ||
47 | "message_hash": "XKQMJ4CNTXBFE1V2WR6JS063J7PZQE4XMB5JH3RS5X0THQ1JQSQ69Y7KDBC9TYRJEZH48MEPY2SF4QHQ4VHXC0YQX5935MQEGP0AX6R", | ||
48 | "rsa_public_key": "040000YRN1NVJ68RS6RJF52PGRCQG19ZKWQPSTJX2G7ZDCKSZFE2VW3HHA81YF5C639JHJF5TX8YTEE2FW2WQCG1PTKNBSPPJEJGA032CN3E8QZ27VWY0K6JFT8ZSYWRH2SKDMXW56A4QKY46JJBWJ6T0ZRVBW6S1HTHXVE2RW8MXRW5T801077MDY13N5F8Z1JZVKBJ06TK3S0YPEDBXK0VEHRHEQJ5X5XYKR4KQTFAZNBMKXY8836VCHBXTK4YNX6AJ1CK29SMJH3Z3QRM16A2TNQGFR0HSMV446BF7FMT2E379ZAT5ST4G3BM2NWZYW545S2SW5MG5S6M88XZZ7SKFD48YVXNZ205GGSEYJPVBMR76WG4ZG30WBCPC1N54XE12RMAG81D8C09WG22PKGGDHYXX68N04002", | ||
49 | "rsa_private_key": "50W3MTV5F4PP8RBMC4520A1H60X70XB2DHMP6BBBCNWGM81050SKMWKKC452081050RKMVHJ6MVKM06RN1NVJ68RS6RJF52PGRCQG19ZKWQPSTJX2G7ZDCKSZFE2VW3HHA81YF5C639JHJF5TX8YTEE2FW2WQCG1PTKNBSPPJEJGA032CN3E8QZ27VWY0K6JFT8ZSYWRH2SKDMXW56A4QKY46JJBWJ6T0ZRVBW6S1HTHXVE2RW8MXRW5T801077MDY13N5F8Z1JZVKBJ06TK3S0YPEDBXK0VEHRHEQJ5X5XYKR4KQTFAZNBMKXY8836VCHBXTK4YNX6AJ1CK29SMJH3Z3QRM16A2TNQGFR0HSMV446BF7FMT2E379ZAT5ST4G3BM2NWZYW545S2SW5MG5S6M88XZZ7SKFD48YVXNZ205GGSEYJPVBMR76WG4ZG30WBCPC1N54XE12RMAG81D8C09WG22PKGGDHYXX68N5452081050RKMS9K780G009918G2081918G20A8A40M32C9TE1S6JXK1EHJJTTV5F45208186CX74WV118G2081864X6WCHN6WX01P58DEWHJ669P4KS8NM635W0AFWZ5XPEMQ8M1ZVB4YFVVGPZ0WCAJ0FKSB1GTCMCKSEQA7PKKGKZ0Q5V40DPMXAYDNMKMM2G0RK58VJ5ZRHYZ7G4SMKYJ7YFQ648PCVD7F19JH5WZH1MMJZ4HPG7Y6TZ1P8CEMFEVGP7257E71EJ0081SX3FG8X9BT7RCQYWTWG1PMRY87NKKAZCR6VME4BNWHF9FFMY14XYKTQXAX4ZFJ20SPV4AZEMS7NF9JMGB4RJED4M8ZRXY509JGPNDW3Y04ED6S11JVSVX6GKGSTFTPHEEH40TX0NF7ZQ191E8PF1D41E9N227FZSYCVV927PZDFRG1C46BQMNPTX61SQ417W0R72V5K0D997BG8P52M20BA302F40GNMW43CFQF9J59918G2081864X6ACST04002A8A40G20A1H79J34D9P78SBPDS45EQZVX2D6R1GZ6FHKHDG4ABYZ6FCPSMMGZ29ZY5NS8B304D0QBD800A5SM3D8S92WKS3KFWFXE8X597TNJBR94BGNCFHY8TV63KQZ0RFMRTPHFA2XNFX2V4KECZNTHGKZNJTWH8051XP6FJJ6700A5KWMH24CK1KF9XVH2ATQJ4F1ZJZQM00A00ESTVKM5N58K5FN5TW34B1H1Q1CWRZXJ9QMBG0QKHXHZMFYZSN9ERV59935716NGSX9GBB3R9BY32TBTAJ7K0N391ATSE5263X5NKVDH7XCF1PF0WRTTJYBAJBQWVGJ8P7RGVWB27Q3S4AJJ55FS2T9K341EHATHJZQQTR0JWP5E4NQHR4GN60QMK2SY0VJM56N0NHJXRGAGJW3S9ABJ7M4TAV6YRJJ2H040G2GC9TE0RK4E9T03CET00DAJDXJ1NF511J69JMDDSVARPSFSS6AJTRMVXD10NVA4Z162ER74WNH0J6H2RTA9C9MZM4TEBP8P7AF0NANBH4XZJCNDZV7BEAMJ6R363GHZKGBR0HH1SATGWNJWGW2961MRFPX7S0RJK7CWYE7EX1XQ2683TENRPDKV29D1F1RNDD6P04MZTKSH5YMEZPKGEDRJZXXM9918G2081864X72C9J74X01ZXF256C681JDWRRG7T4AC9JRHP54YM65X9AT17YGC3G4569ZKBQEZ219Y7JG4V10F4AW7Q1XKGPTW3Y3X0WCTB06HM249Q094VHEYD4BTEQ2DF334W8G3VEB7N1CHCMPZEVVTR0XWQSNV6MBJRF5MBY6Z148YA36YA61EXA28PHE3KSN1M4HVF7B06JD736TSK9GC9CAG8F0MMGM81040M32EKN64S3JEG0YCC3PHPNBZAGQ2WE8HJHM28B049N2MQ94G8VPXNHG43C6ADB44DE1DK8ASMYTSXZWMAYKN1SS2GVFTVH8Q4N3TTJ3BKA61Y93QE17XNRF3S0CSSQTY3SVVAFHDA9APFFSMRYD67N4DXQ1X42NA270VF6H0MCRQ87JKVBQAB0CH2WH7RHYMCQ4KN50M2NEFE1YA6F2N2DG7Z9JA8A40G20A8A40G2J2H054500", | ||
50 | "blinding_key_secret": "3SWF49XZPHQMENTSBZQR7Z0B8ZSZ2JRARE79Q4VXZMQ7W6QABXMG", | ||
51 | "blinded_message": "3KHKZJZ30ABB4E56MA2V0EQWGCWH0QQG9P2ZHYHR186C5HZXJMM4N9WXAQTKS94QSV9Y17GGNXN5MB1PZZFG7Q0FY88QPKKRG4MYCPSMTZK5W59R0MJVNJ4P4AQM96TDG5W7RV8GSNR1QQZ1GNHW3CX6D6ZRTMXB2NKB5SSYTDJS79F5ZFBRZ4HVED9JBBPWSR79KVV5QQ4APBGHBCKGMF9NJJS53A1BVYHDEVYAGFYF2SNEP827ZP50FKJ5GKGV8NQ15ESEZ69AT7GJG0T3TZVENY2YN9CVR98W3BKEZ53J7VTANARG8SJS8AMJQ7S23P5HRJ7XE9KTNRNXKH49MXV9JHHYE5535N7AGWEKR47SBCGNF44Z7XJ9RV5BQV12ZRJKN4HBZQHDNCMH3QKX9Z6G64", | ||
52 | "blinded_sig": "ND1V807BK0G73SDXN582BP3Q21MWF4A76EGWD0KA3XGJAWPSVHNHKA44931ZRB9M76SYAFD8ZPTG3A7FH5G2CWGX76VXTCDX5XNRW7EEBNMPDAQ0ZEKF6AHP872SKCGRH89SK4NGC57M8BRA3ZRPDDT9XCBG3XY02VQH4Z0F39DPBS48K0EBMK7B9S3X6QDNR5ND5MV0G7G7T3VPKZRW94MQBKPY1T6K53MQGG4PV81D9YEWNRM3WE04NNQREYDA5ETVDWQ5ZCYV9HF4ZCMWVVGWDBDH732JA3NKZ2B8QK0E6XS0Y4GGGQJS6HFQ4PATGK3TS5GHJEPDF3A6XAFNJQV99CSJW7V1NC504NTQ5NJ8KAVC1758MBBV3SS2BND4YHF0Y4NWJNVH3STV166YWFKR8W", | ||
53 | "sig": "EC5MVSPGQMM96N2VT4R2G5104E61V8RY4PR6AK9F614TVVEN7D152T0DP97CDTRDDSQGBV4GZWXQPM90SW30R2RAKKHNDCXHQFAMRSW1XCBEKVKBGC6FP0AQY9S37NVR01VJ2WVX8PN29H2ZFFQBQ9JK96GTJZ3B7DD583S8Y93GH5KWEM41CZJ73QCRT1A2AGVXX5ACFR0T448MC81QB4EGCKP5Z96VCX6RPDD5S9A4295M0E9PPQJCN5G5JKWKG17HWEDF4A26ZMD8YW27EQBZ69GSEZX4PWEV7AXFGG5X0RPKCQEPCX7XDY6NXJ1E2FZBX259RDRCFNDAZS80T0DHD9NVE73QDDESZYEZTM1TM669GHPN8AF4QV8DNW7SFZZKJ67FWR8CZC0PWTEN4ZPTRM" | ||
54 | } | ||
55 | ] | ||
56 | } | ||
diff --git a/src/cli/util/gnunet-base32.c b/src/cli/util/gnunet-base32.c new file mode 100644 index 000000000..209741740 --- /dev/null +++ b/src/cli/util/gnunet-base32.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-base32.c | ||
23 | * @brief tool to encode/decode from/to the Crockford Base32 encoding GNUnet uses | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * The main function of gnunet-base32 | ||
33 | * | ||
34 | * @param argc number of arguments from the command line | ||
35 | * @param argv command line arguments | ||
36 | * @return 0 ok, 1 on error | ||
37 | */ | ||
38 | int | ||
39 | main (int argc, | ||
40 | char *const *argv) | ||
41 | { | ||
42 | int decode = 0; | ||
43 | const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
44 | GNUNET_GETOPT_option_flag ('d', | ||
45 | "decode", | ||
46 | gettext_noop ( | ||
47 | "run decoder modus, otherwise runs as encoder"), | ||
48 | &decode), | ||
49 | GNUNET_GETOPT_option_help ("Crockford base32 encoder/decoder"), | ||
50 | GNUNET_GETOPT_option_version (PACKAGE_VERSION), | ||
51 | GNUNET_GETOPT_OPTION_END | ||
52 | }; | ||
53 | int ret; | ||
54 | char *in; | ||
55 | unsigned int in_size; | ||
56 | ssize_t iret; | ||
57 | char *out; | ||
58 | size_t out_size; | ||
59 | |||
60 | if (GNUNET_OK != | ||
61 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
62 | &argc, &argv)) | ||
63 | return 2; | ||
64 | ret = GNUNET_GETOPT_run ("gnunet-base32", | ||
65 | options, | ||
66 | argc, | ||
67 | argv); | ||
68 | if (ret < 0) | ||
69 | return 1; | ||
70 | if (0 == ret) | ||
71 | return 0; | ||
72 | in_size = 0; | ||
73 | in = NULL; | ||
74 | iret = 1; | ||
75 | while (iret > 0) | ||
76 | { | ||
77 | /* read in blocks of 4k */ | ||
78 | char buf[4092]; | ||
79 | |||
80 | iret = read (0, | ||
81 | buf, | ||
82 | sizeof (buf)); | ||
83 | if (iret < 0) | ||
84 | { | ||
85 | GNUNET_free (in); | ||
86 | return 2; | ||
87 | } | ||
88 | if (iret > 0) | ||
89 | { | ||
90 | if (iret + in_size < in_size) | ||
91 | { | ||
92 | GNUNET_break (0); | ||
93 | GNUNET_free (in); | ||
94 | return 1; | ||
95 | } | ||
96 | GNUNET_array_grow (in, | ||
97 | in_size, | ||
98 | in_size + iret); | ||
99 | memcpy (&in[in_size - iret], | ||
100 | buf, | ||
101 | iret); | ||
102 | } | ||
103 | } | ||
104 | if (decode) | ||
105 | { | ||
106 | /* This formula can overestimate by 1 byte, so we try both | ||
107 | out_size and out_size-1 below */ | ||
108 | out_size = in_size * 5 / 8; | ||
109 | out = GNUNET_malloc (out_size); | ||
110 | if ( (GNUNET_OK != | ||
111 | GNUNET_STRINGS_string_to_data (in, | ||
112 | in_size, | ||
113 | out, | ||
114 | out_size)) && | ||
115 | (out_size > 0) ) | ||
116 | { | ||
117 | out_size--; | ||
118 | if (GNUNET_OK != | ||
119 | GNUNET_STRINGS_string_to_data (in, | ||
120 | in_size, | ||
121 | out, | ||
122 | out_size)) | ||
123 | { | ||
124 | GNUNET_free (out); | ||
125 | GNUNET_free (in); | ||
126 | return 3; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | out = GNUNET_STRINGS_data_to_string_alloc (in, | ||
133 | in_size); | ||
134 | out_size = strlen (out); | ||
135 | } | ||
136 | { | ||
137 | size_t pos = 0; | ||
138 | |||
139 | while (pos < out_size) | ||
140 | { | ||
141 | iret = write (1, | ||
142 | &out[pos], | ||
143 | out_size - pos); | ||
144 | if (iret <= 0) | ||
145 | return 4; | ||
146 | pos += iret; | ||
147 | } | ||
148 | } | ||
149 | GNUNET_free (out); | ||
150 | GNUNET_free_nz ((void *) argv); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | |||
155 | /* end of gnunet-uri.c */ | ||
diff --git a/src/cli/util/gnunet-config-diff.c b/src/cli/util/gnunet-config-diff.c new file mode 100644 index 000000000..e1e3ffd5d --- /dev/null +++ b/src/cli/util/gnunet-config-diff.c | |||
@@ -0,0 +1,24 @@ | |||
1 | |||
2 | #include "platform.h" | ||
3 | #include <gnunet_util_lib.h> | ||
4 | |||
5 | int | ||
6 | main (int argc, char **argv) | ||
7 | { | ||
8 | struct GNUNET_CONFIGURATION_Handle *i1; | ||
9 | struct GNUNET_CONFIGURATION_Handle *i2; | ||
10 | |||
11 | if (argc != 3) | ||
12 | { | ||
13 | fprintf (stderr, "Invoke using `%s DEFAULTS-IN DIFFS'\n", argv[0]); | ||
14 | return 1; | ||
15 | } | ||
16 | i1 = GNUNET_CONFIGURATION_create (); | ||
17 | i2 = GNUNET_CONFIGURATION_create (); | ||
18 | if ((GNUNET_OK != GNUNET_CONFIGURATION_load (i1, argv[1])) || | ||
19 | (GNUNET_OK != GNUNET_CONFIGURATION_load (i2, argv[2]))) | ||
20 | return 1; | ||
21 | if (GNUNET_OK != GNUNET_CONFIGURATION_write_diffs (i1, i2, argv[2])) | ||
22 | return 2; | ||
23 | return 0; | ||
24 | } | ||
diff --git a/src/cli/util/gnunet-config.c b/src/cli/util/gnunet-config.c new file mode 100644 index 000000000..714c683dd --- /dev/null +++ b/src/cli/util/gnunet-config.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-config.c | ||
23 | * @brief tool to access and manipulate GNUnet configuration files | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Backend to check if the respective plugin is | ||
33 | * loadable. NULL if no check is to be performed. | ||
34 | * The value is the "basename" of the plugin to load. | ||
35 | */ | ||
36 | static char *backend_check; | ||
37 | |||
38 | |||
39 | /** | ||
40 | * If printing the value of CFLAGS has been requested. | ||
41 | */ | ||
42 | static int cflags; | ||
43 | |||
44 | /** | ||
45 | * Check if this is an experimental build | ||
46 | */ | ||
47 | static int is_experimental; | ||
48 | |||
49 | |||
50 | /** | ||
51 | * If printing the value of LIBS has been requested. | ||
52 | */ | ||
53 | static int libs; | ||
54 | |||
55 | |||
56 | /** | ||
57 | * If printing the value of PREFIX has been requested. | ||
58 | */ | ||
59 | static int prefix; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * Print each option in a given section. | ||
64 | * Main task to run to perform operations typical for | ||
65 | * gnunet-config as per the configuration settings | ||
66 | * given in @a cls. | ||
67 | * | ||
68 | * @param cls closure with the `struct GNUNET_CONFIGURATION_ConfigSettings` | ||
69 | * @param args remaining command-line arguments | ||
70 | * @param cfgfile name of the configuration file used (for saving, | ||
71 | * can be NULL!) | ||
72 | * @param cfg configuration | ||
73 | */ | ||
74 | static void | ||
75 | run (void *cls, | ||
76 | char *const *args, | ||
77 | const char *cfgfile, | ||
78 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
79 | { | ||
80 | struct GNUNET_CONFIGURATION_ConfigSettings *cs = cls; | ||
81 | |||
82 | if (1 == is_experimental) | ||
83 | { | ||
84 | #ifdef GNUNET_EXPERIMENTAL | ||
85 | cs->global_ret = 0; | ||
86 | #else | ||
87 | cs->global_ret = 1; | ||
88 | #endif | ||
89 | return; | ||
90 | } | ||
91 | if (1 == cflags || 1 == libs || 1 == prefix) | ||
92 | { | ||
93 | char *prefixdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_PREFIX); | ||
94 | char *libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); | ||
95 | |||
96 | if (1 == cflags) | ||
97 | { | ||
98 | fprintf (stdout, "-I%sinclude\n", prefixdir); | ||
99 | } | ||
100 | if (1 == libs) | ||
101 | { | ||
102 | fprintf (stdout, "-L%s -lgnunetutil\n", libdir); | ||
103 | } | ||
104 | if (1 == prefix) | ||
105 | { | ||
106 | fprintf (stdout, "%s\n", prefixdir); | ||
107 | } | ||
108 | cs->global_ret = 0; | ||
109 | GNUNET_free (prefixdir); | ||
110 | GNUNET_free (libdir); | ||
111 | return; | ||
112 | } | ||
113 | if (NULL != backend_check) | ||
114 | { | ||
115 | char *name; | ||
116 | |||
117 | GNUNET_asprintf (&name, | ||
118 | "libgnunet_plugin_%s", | ||
119 | backend_check); | ||
120 | cs->global_ret = (GNUNET_OK == | ||
121 | GNUNET_PLUGIN_test (name)) ? 0 : 77; | ||
122 | GNUNET_free (name); | ||
123 | return; | ||
124 | } | ||
125 | GNUNET_CONFIGURATION_config_tool_run (cs, | ||
126 | args, | ||
127 | cfgfile, | ||
128 | cfg); | ||
129 | } | ||
130 | |||
131 | |||
132 | /** | ||
133 | * Program to manipulate configuration files. | ||
134 | * | ||
135 | * @param argc number of arguments from the command line | ||
136 | * @param argv command line arguments | ||
137 | * @return 0 ok, 1 on error | ||
138 | */ | ||
139 | int | ||
140 | main (int argc, | ||
141 | char *const *argv) | ||
142 | { | ||
143 | struct GNUNET_CONFIGURATION_ConfigSettings cs = { | ||
144 | .api_version = GNUNET_UTIL_VERSION, | ||
145 | .global_ret = EXIT_SUCCESS | ||
146 | }; | ||
147 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
148 | GNUNET_GETOPT_option_exclusive ( | ||
149 | GNUNET_GETOPT_option_string ( | ||
150 | 'b', | ||
151 | "supported-backend", | ||
152 | "BACKEND", | ||
153 | gettext_noop ( | ||
154 | "test if the current installation supports the specified BACKEND"), | ||
155 | &backend_check)), | ||
156 | GNUNET_GETOPT_option_flag ( | ||
157 | 'C', | ||
158 | "cflags", | ||
159 | gettext_noop ( | ||
160 | "Provide an appropriate value for CFLAGS to applications building on top of GNUnet"), | ||
161 | &cflags), | ||
162 | GNUNET_GETOPT_option_flag ( | ||
163 | 'E', | ||
164 | "is-experimental", | ||
165 | gettext_noop ("Is this an experimental build of GNUnet"), | ||
166 | &is_experimental), | ||
167 | GNUNET_GETOPT_option_flag ( | ||
168 | 'j', | ||
169 | "libs", | ||
170 | gettext_noop ( | ||
171 | "Provide an appropriate value for LIBS to applications building on top of GNUnet"), | ||
172 | &libs), | ||
173 | GNUNET_GETOPT_option_flag ( | ||
174 | 'p', | ||
175 | "prefix", | ||
176 | gettext_noop ( | ||
177 | "Provide the path under which GNUnet was installed"), | ||
178 | &prefix), | ||
179 | GNUNET_CONFIGURATION_CONFIG_OPTIONS (&cs), | ||
180 | GNUNET_GETOPT_OPTION_END | ||
181 | }; | ||
182 | enum GNUNET_GenericReturnValue ret; | ||
183 | |||
184 | if (GNUNET_OK != | ||
185 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
186 | &argc, &argv)) | ||
187 | return EXIT_FAILURE; | ||
188 | ret = | ||
189 | GNUNET_PROGRAM_run (argc, | ||
190 | argv, | ||
191 | "gnunet-config [OPTIONS]", | ||
192 | gettext_noop ("Manipulate GNUnet configuration files"), | ||
193 | options, | ||
194 | &run, | ||
195 | &cs); | ||
196 | GNUNET_free_nz ((void *) argv); | ||
197 | GNUNET_CONFIGURATION_config_settings_free (&cs); | ||
198 | if (GNUNET_NO == ret) | ||
199 | return 0; | ||
200 | if (GNUNET_SYSERR == ret) | ||
201 | return EXIT_INVALIDARGUMENT; | ||
202 | return cs.global_ret; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* end of gnunet-config.c */ | ||
diff --git a/src/cli/util/gnunet-crypto-tvg.c b/src/cli/util/gnunet-crypto-tvg.c new file mode 100644 index 000000000..721177eda --- /dev/null +++ b/src/cli/util/gnunet-crypto-tvg.c | |||
@@ -0,0 +1,1608 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2020 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-crypto-tgv.c | ||
23 | * @brief Generate test vectors for cryptographic operations. | ||
24 | * @author Florian Dold | ||
25 | * | ||
26 | * Note that this program shouldn't depend on code in src/json/, | ||
27 | * so we're using raw jansson and no GNUnet JSON helpers. | ||
28 | * | ||
29 | * Test vectors have the following format (TypeScript pseudo code): | ||
30 | * | ||
31 | * interface TestVectorFile { | ||
32 | * encoding: "base32crockford"; | ||
33 | * producer?: string; | ||
34 | * vectors: TestVector[]; | ||
35 | * } | ||
36 | * | ||
37 | * enum Operation { | ||
38 | * Hash("hash"), | ||
39 | * ... | ||
40 | * } | ||
41 | * | ||
42 | * interface TestVector { | ||
43 | * operation: Operation; | ||
44 | * // Inputs for the operation | ||
45 | * [ k: string]: string | number; | ||
46 | * }; | ||
47 | * | ||
48 | * | ||
49 | */ | ||
50 | |||
51 | #include "platform.h" | ||
52 | #include "gnunet_util_lib.h" | ||
53 | #include "gnunet_signatures.h" | ||
54 | #include "gnunet_testing_lib.h" | ||
55 | #include <jansson.h> | ||
56 | #include <gcrypt.h> | ||
57 | |||
58 | GNUNET_NETWORK_STRUCT_BEGIN | ||
59 | |||
60 | /** | ||
61 | * Sample signature struct. | ||
62 | * | ||
63 | * Purpose is #GNUNET_SIGNATURE_PURPOSE_TEST | ||
64 | */ | ||
65 | struct TestSignatureDataPS | ||
66 | { | ||
67 | struct GNUNET_CRYPTO_EccSignaturePurpose purpose; | ||
68 | uint32_t testval; | ||
69 | }; | ||
70 | |||
71 | GNUNET_NETWORK_STRUCT_END | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Should we verify or output test vectors? | ||
76 | */ | ||
77 | static int verify_flag = GNUNET_NO; | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Global exit code. | ||
82 | */ | ||
83 | static int global_ret = 0; | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Create a fresh test vector for a given operation label. | ||
88 | * | ||
89 | * @param vecs array of vectors to append the new vector to | ||
90 | * @param vecname label for the operation of the vector | ||
91 | * @returns the fresh test vector | ||
92 | */ | ||
93 | static json_t * | ||
94 | vec_for (json_t *vecs, const char *vecname) | ||
95 | { | ||
96 | json_t *t = json_object (); | ||
97 | |||
98 | json_object_set_new (t, | ||
99 | "operation", | ||
100 | json_string (vecname)); | ||
101 | json_array_append_new (vecs, t); | ||
102 | return t; | ||
103 | } | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Add a base32crockford encoded value | ||
108 | * to a test vector. | ||
109 | * | ||
110 | * @param vec test vector to add to | ||
111 | * @param label label for the value | ||
112 | * @param data data to add | ||
113 | * @param size size of data | ||
114 | */ | ||
115 | static void | ||
116 | d2j (json_t *vec, | ||
117 | const char *label, | ||
118 | const void *data, | ||
119 | size_t size) | ||
120 | { | ||
121 | char *buf; | ||
122 | json_t *json; | ||
123 | |||
124 | buf = GNUNET_STRINGS_data_to_string_alloc (data, size); | ||
125 | json = json_string (buf); | ||
126 | GNUNET_free (buf); | ||
127 | GNUNET_break (NULL != json); | ||
128 | |||
129 | json_object_set_new (vec, label, json); | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Add a number to a test vector. | ||
135 | * | ||
136 | * @param vec test vector to add to | ||
137 | * @param label label for the value | ||
138 | * @param data data to add | ||
139 | * @param size size of data | ||
140 | */ | ||
141 | static void | ||
142 | uint2j (json_t *vec, | ||
143 | const char *label, | ||
144 | unsigned int num) | ||
145 | { | ||
146 | json_t *json = json_integer (num); | ||
147 | |||
148 | json_object_set_new (vec, label, json); | ||
149 | } | ||
150 | |||
151 | |||
152 | static int | ||
153 | expect_data_fixed (json_t *vec, | ||
154 | const char *name, | ||
155 | void *data, | ||
156 | size_t expect_len) | ||
157 | { | ||
158 | const char *s = json_string_value (json_object_get (vec, name)); | ||
159 | |||
160 | if (NULL == s) | ||
161 | return GNUNET_NO; | ||
162 | |||
163 | if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s, | ||
164 | strlen (s), | ||
165 | data, | ||
166 | expect_len)) | ||
167 | return GNUNET_NO; | ||
168 | return GNUNET_OK; | ||
169 | } | ||
170 | |||
171 | |||
172 | static int | ||
173 | expect_data_dynamic (json_t *vec, | ||
174 | const char *name, | ||
175 | void **data, | ||
176 | size_t *ret_len) | ||
177 | { | ||
178 | const char *s = json_string_value (json_object_get (vec, name)); | ||
179 | char *tmp; | ||
180 | size_t len; | ||
181 | |||
182 | if (NULL == s) | ||
183 | return GNUNET_NO; | ||
184 | |||
185 | len = (strlen (s) * 5) / 8; | ||
186 | if (NULL != ret_len) | ||
187 | *ret_len = len; | ||
188 | tmp = GNUNET_malloc (len); | ||
189 | |||
190 | if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s, strlen (s), tmp, len)) | ||
191 | { | ||
192 | GNUNET_free (tmp); | ||
193 | return GNUNET_NO; | ||
194 | } | ||
195 | *data = tmp; | ||
196 | return GNUNET_OK; | ||
197 | } | ||
198 | |||
199 | |||
200 | /** | ||
201 | * Check a single vector. | ||
202 | * | ||
203 | * @param operation operator of the vector | ||
204 | * @param vec the vector, a JSON object. | ||
205 | * | ||
206 | * @returns GNUNET_OK if the vector is okay | ||
207 | */ | ||
208 | static int | ||
209 | checkvec (const char *operation, | ||
210 | json_t *vec) | ||
211 | { | ||
212 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
213 | "checking %s\n", operation); | ||
214 | |||
215 | if (0 == strcmp (operation, "hash")) | ||
216 | { | ||
217 | void *data; | ||
218 | size_t data_len; | ||
219 | struct GNUNET_HashCode hash_out; | ||
220 | struct GNUNET_HashCode hc; | ||
221 | |||
222 | if (GNUNET_OK != expect_data_dynamic (vec, | ||
223 | "input", | ||
224 | &data, | ||
225 | &data_len)) | ||
226 | { | ||
227 | GNUNET_break (0); | ||
228 | return GNUNET_SYSERR; | ||
229 | } | ||
230 | if (GNUNET_OK != expect_data_fixed (vec, | ||
231 | "output", | ||
232 | &hash_out, | ||
233 | sizeof (hash_out))) | ||
234 | { | ||
235 | GNUNET_free (data); | ||
236 | GNUNET_break (0); | ||
237 | return GNUNET_NO; | ||
238 | } | ||
239 | |||
240 | GNUNET_CRYPTO_hash (data, data_len, &hc); | ||
241 | |||
242 | if (0 != GNUNET_memcmp (&hc, &hash_out)) | ||
243 | { | ||
244 | GNUNET_free (data); | ||
245 | GNUNET_break (0); | ||
246 | return GNUNET_NO; | ||
247 | } | ||
248 | GNUNET_free (data); | ||
249 | } | ||
250 | else if (0 == strcmp (operation, "ecc_ecdh")) | ||
251 | { | ||
252 | struct GNUNET_CRYPTO_EcdhePrivateKey priv1; | ||
253 | struct GNUNET_CRYPTO_EcdhePublicKey pub1; | ||
254 | struct GNUNET_CRYPTO_EcdhePrivateKey priv2; | ||
255 | struct GNUNET_HashCode skm; | ||
256 | struct GNUNET_HashCode skm_comp; | ||
257 | |||
258 | if (GNUNET_OK != expect_data_fixed (vec, | ||
259 | "priv1", | ||
260 | &priv1, | ||
261 | sizeof (priv1))) | ||
262 | { | ||
263 | GNUNET_break (0); | ||
264 | return GNUNET_NO; | ||
265 | } | ||
266 | if (GNUNET_OK != expect_data_fixed (vec, | ||
267 | "priv2", | ||
268 | &priv2, | ||
269 | sizeof (priv2))) | ||
270 | { | ||
271 | GNUNET_break (0); | ||
272 | return GNUNET_NO; | ||
273 | } | ||
274 | if (GNUNET_OK != expect_data_fixed (vec, | ||
275 | "pub1", | ||
276 | &pub1, | ||
277 | sizeof (pub1))) | ||
278 | { | ||
279 | GNUNET_break (0); | ||
280 | return GNUNET_NO; | ||
281 | } | ||
282 | if (GNUNET_OK != expect_data_fixed (vec, | ||
283 | "skm", | ||
284 | &skm, | ||
285 | sizeof (skm))) | ||
286 | { | ||
287 | GNUNET_break (0); | ||
288 | return GNUNET_NO; | ||
289 | } | ||
290 | GNUNET_assert (GNUNET_OK == | ||
291 | GNUNET_CRYPTO_ecc_ecdh (&priv2, | ||
292 | &pub1, | ||
293 | &skm_comp)); | ||
294 | if (0 != GNUNET_memcmp (&skm, &skm_comp)) | ||
295 | { | ||
296 | GNUNET_break (0); | ||
297 | return GNUNET_NO; | ||
298 | } | ||
299 | } | ||
300 | else if (0 == strcmp (operation, "eddsa_key_derivation")) | ||
301 | { | ||
302 | struct GNUNET_CRYPTO_EddsaPrivateKey priv; | ||
303 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
304 | struct GNUNET_CRYPTO_EddsaPublicKey pub_comp; | ||
305 | |||
306 | if (GNUNET_OK != expect_data_fixed (vec, | ||
307 | "priv", | ||
308 | &priv, | ||
309 | sizeof (priv))) | ||
310 | { | ||
311 | GNUNET_break (0); | ||
312 | return GNUNET_NO; | ||
313 | } | ||
314 | |||
315 | if (GNUNET_OK != expect_data_fixed (vec, | ||
316 | "pub", | ||
317 | &pub, | ||
318 | sizeof (pub))) | ||
319 | { | ||
320 | GNUNET_break (0); | ||
321 | return GNUNET_NO; | ||
322 | } | ||
323 | |||
324 | GNUNET_CRYPTO_eddsa_key_get_public (&priv, | ||
325 | &pub_comp); | ||
326 | if (0 != GNUNET_memcmp (&pub, &pub_comp)) | ||
327 | { | ||
328 | GNUNET_break (0); | ||
329 | return GNUNET_NO; | ||
330 | } | ||
331 | |||
332 | } | ||
333 | else if (0 == strcmp (operation, "eddsa_signing")) | ||
334 | { | ||
335 | struct GNUNET_CRYPTO_EddsaPrivateKey priv; | ||
336 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
337 | struct TestSignatureDataPS data = { 0 }; | ||
338 | struct GNUNET_CRYPTO_EddsaSignature sig; | ||
339 | struct GNUNET_CRYPTO_EddsaSignature sig_comp; | ||
340 | |||
341 | if (GNUNET_OK != expect_data_fixed (vec, | ||
342 | "priv", | ||
343 | &priv, | ||
344 | sizeof (priv))) | ||
345 | { | ||
346 | GNUNET_break (0); | ||
347 | return GNUNET_NO; | ||
348 | } | ||
349 | |||
350 | if (GNUNET_OK != expect_data_fixed (vec, | ||
351 | "pub", | ||
352 | &pub, | ||
353 | sizeof (pub))) | ||
354 | { | ||
355 | GNUNET_break (0); | ||
356 | return GNUNET_NO; | ||
357 | } | ||
358 | |||
359 | if (GNUNET_OK != expect_data_fixed (vec, | ||
360 | "data", | ||
361 | &data, | ||
362 | sizeof (data))) | ||
363 | { | ||
364 | GNUNET_break (0); | ||
365 | return GNUNET_NO; | ||
366 | } | ||
367 | |||
368 | if (GNUNET_OK != expect_data_fixed (vec, | ||
369 | "sig", | ||
370 | &sig, | ||
371 | sizeof (sig))) | ||
372 | { | ||
373 | GNUNET_break (0); | ||
374 | return GNUNET_NO; | ||
375 | } | ||
376 | |||
377 | GNUNET_CRYPTO_eddsa_sign (&priv, | ||
378 | &data, | ||
379 | &sig_comp); | ||
380 | GNUNET_assert (GNUNET_OK == | ||
381 | GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST, | ||
382 | &data, | ||
383 | &sig, | ||
384 | &pub)); | ||
385 | if (0 != GNUNET_memcmp (&sig, &sig_comp)) | ||
386 | { | ||
387 | GNUNET_break (0); | ||
388 | return GNUNET_NO; | ||
389 | } | ||
390 | } | ||
391 | else if (0 == strcmp (operation, "kdf")) | ||
392 | { | ||
393 | size_t out_len; | ||
394 | void *out; | ||
395 | size_t out_len_comp; | ||
396 | void *out_comp; | ||
397 | void *ikm; | ||
398 | size_t ikm_len; | ||
399 | void *salt; | ||
400 | size_t salt_len; | ||
401 | void *ctx; | ||
402 | size_t ctx_len; | ||
403 | |||
404 | if (GNUNET_OK != expect_data_dynamic (vec, | ||
405 | "out", | ||
406 | &out, | ||
407 | &out_len)) | ||
408 | { | ||
409 | GNUNET_break (0); | ||
410 | return GNUNET_SYSERR; | ||
411 | } | ||
412 | |||
413 | out_len_comp = out_len; | ||
414 | out_comp = GNUNET_malloc (out_len_comp); | ||
415 | |||
416 | if (GNUNET_OK != expect_data_dynamic (vec, | ||
417 | "ikm", | ||
418 | &ikm, | ||
419 | &ikm_len)) | ||
420 | { | ||
421 | GNUNET_free (out); | ||
422 | GNUNET_free (out_comp); | ||
423 | GNUNET_break (0); | ||
424 | return GNUNET_SYSERR; | ||
425 | } | ||
426 | |||
427 | if (GNUNET_OK != expect_data_dynamic (vec, | ||
428 | "salt", | ||
429 | &salt, | ||
430 | &salt_len)) | ||
431 | { | ||
432 | GNUNET_free (out); | ||
433 | GNUNET_free (out_comp); | ||
434 | GNUNET_free (ikm); | ||
435 | GNUNET_break (0); | ||
436 | return GNUNET_SYSERR; | ||
437 | } | ||
438 | |||
439 | if (GNUNET_OK != expect_data_dynamic (vec, | ||
440 | "ctx", | ||
441 | &ctx, | ||
442 | &ctx_len)) | ||
443 | { | ||
444 | GNUNET_free (out); | ||
445 | GNUNET_free (out_comp); | ||
446 | GNUNET_free (ikm); | ||
447 | GNUNET_free (salt); | ||
448 | GNUNET_break (0); | ||
449 | return GNUNET_SYSERR; | ||
450 | } | ||
451 | |||
452 | GNUNET_assert (GNUNET_OK == | ||
453 | GNUNET_CRYPTO_kdf (out_comp, | ||
454 | out_len_comp, | ||
455 | salt, | ||
456 | salt_len, | ||
457 | ikm, | ||
458 | ikm_len, | ||
459 | ctx, | ||
460 | ctx_len, | ||
461 | NULL)); | ||
462 | |||
463 | if (0 != memcmp (out, out_comp, out_len)) | ||
464 | { | ||
465 | GNUNET_free (out); | ||
466 | GNUNET_free (out_comp); | ||
467 | GNUNET_free (ikm); | ||
468 | GNUNET_free (salt); | ||
469 | GNUNET_free (ctx); | ||
470 | GNUNET_break (0); | ||
471 | return GNUNET_NO; | ||
472 | } | ||
473 | GNUNET_free (out); | ||
474 | GNUNET_free (out_comp); | ||
475 | GNUNET_free (ikm); | ||
476 | GNUNET_free (salt); | ||
477 | GNUNET_free (ctx); | ||
478 | } | ||
479 | else if (0 == strcmp (operation, "eddsa_ecdh")) | ||
480 | { | ||
481 | struct GNUNET_CRYPTO_EcdhePrivateKey priv_ecdhe; | ||
482 | struct GNUNET_CRYPTO_EcdhePublicKey pub_ecdhe; | ||
483 | struct GNUNET_CRYPTO_EddsaPrivateKey priv_eddsa; | ||
484 | struct GNUNET_CRYPTO_EddsaPublicKey pub_eddsa; | ||
485 | struct GNUNET_HashCode key_material; | ||
486 | struct GNUNET_HashCode key_material_comp; | ||
487 | |||
488 | if (GNUNET_OK != expect_data_fixed (vec, | ||
489 | "priv_ecdhe", | ||
490 | &priv_ecdhe, | ||
491 | sizeof (priv_ecdhe))) | ||
492 | { | ||
493 | GNUNET_break (0); | ||
494 | return GNUNET_NO; | ||
495 | } | ||
496 | |||
497 | if (GNUNET_OK != expect_data_fixed (vec, | ||
498 | "pub_ecdhe", | ||
499 | &pub_ecdhe, | ||
500 | sizeof (pub_ecdhe))) | ||
501 | { | ||
502 | GNUNET_break (0); | ||
503 | return GNUNET_NO; | ||
504 | } | ||
505 | |||
506 | if (GNUNET_OK != expect_data_fixed (vec, | ||
507 | "priv_eddsa", | ||
508 | &priv_eddsa, | ||
509 | sizeof (priv_eddsa))) | ||
510 | { | ||
511 | GNUNET_break (0); | ||
512 | return GNUNET_NO; | ||
513 | } | ||
514 | |||
515 | if (GNUNET_OK != expect_data_fixed (vec, | ||
516 | "pub_eddsa", | ||
517 | &pub_eddsa, | ||
518 | sizeof (pub_eddsa))) | ||
519 | { | ||
520 | GNUNET_break (0); | ||
521 | return GNUNET_NO; | ||
522 | } | ||
523 | |||
524 | if (GNUNET_OK != expect_data_fixed (vec, | ||
525 | "key_material", | ||
526 | &key_material, | ||
527 | sizeof (key_material))) | ||
528 | { | ||
529 | GNUNET_break (0); | ||
530 | return GNUNET_NO; | ||
531 | } | ||
532 | |||
533 | GNUNET_CRYPTO_ecdh_eddsa (&priv_ecdhe, | ||
534 | &pub_eddsa, | ||
535 | &key_material_comp); | ||
536 | |||
537 | if (0 != GNUNET_memcmp (&key_material, | ||
538 | &key_material_comp)) | ||
539 | { | ||
540 | GNUNET_break (0); | ||
541 | return GNUNET_NO; | ||
542 | } | ||
543 | } | ||
544 | else if (0 == strcmp (operation, "rsa_blind_signing")) | ||
545 | { | ||
546 | struct GNUNET_CRYPTO_RsaPrivateKey *skey; | ||
547 | struct GNUNET_CRYPTO_RsaPublicKey *pkey; | ||
548 | struct GNUNET_HashCode message_hash; | ||
549 | struct GNUNET_CRYPTO_RsaBlindingKeySecret bks; | ||
550 | struct GNUNET_CRYPTO_RsaSignature *blinded_sig; | ||
551 | struct GNUNET_CRYPTO_RsaSignature *sig; | ||
552 | struct GNUNET_CRYPTO_RsaBlindedMessage bm; | ||
553 | struct GNUNET_CRYPTO_RsaBlindedMessage bm_comp; | ||
554 | void *public_enc_data; | ||
555 | size_t public_enc_len; | ||
556 | void *secret_enc_data; | ||
557 | size_t secret_enc_len; | ||
558 | void *sig_enc_data; | ||
559 | size_t sig_enc_length; | ||
560 | void *sig_enc_data_comp; | ||
561 | size_t sig_enc_length_comp; | ||
562 | |||
563 | if (GNUNET_OK != | ||
564 | expect_data_fixed (vec, | ||
565 | "message_hash", | ||
566 | &message_hash, | ||
567 | sizeof (message_hash))) | ||
568 | { | ||
569 | GNUNET_break (0); | ||
570 | return GNUNET_SYSERR; | ||
571 | } | ||
572 | |||
573 | if (GNUNET_OK != | ||
574 | expect_data_fixed (vec, | ||
575 | "blinding_key_secret", | ||
576 | &bks, | ||
577 | sizeof (bks))) | ||
578 | { | ||
579 | GNUNET_break (0); | ||
580 | return GNUNET_SYSERR; | ||
581 | } | ||
582 | |||
583 | if (GNUNET_OK != | ||
584 | expect_data_dynamic (vec, | ||
585 | "blinded_message", | ||
586 | &bm.blinded_msg, | ||
587 | &bm.blinded_msg_size)) | ||
588 | { | ||
589 | GNUNET_break (0); | ||
590 | return GNUNET_SYSERR; | ||
591 | } | ||
592 | if (GNUNET_OK != | ||
593 | expect_data_dynamic (vec, | ||
594 | "rsa_public_key", | ||
595 | &public_enc_data, | ||
596 | &public_enc_len)) | ||
597 | { | ||
598 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
599 | GNUNET_break (0); | ||
600 | return GNUNET_SYSERR; | ||
601 | } | ||
602 | if (GNUNET_OK != | ||
603 | expect_data_dynamic (vec, | ||
604 | "rsa_private_key", | ||
605 | &secret_enc_data, | ||
606 | &secret_enc_len)) | ||
607 | { | ||
608 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
609 | GNUNET_free (public_enc_data); | ||
610 | GNUNET_break (0); | ||
611 | return GNUNET_SYSERR; | ||
612 | } | ||
613 | if (GNUNET_OK != | ||
614 | expect_data_dynamic (vec, | ||
615 | "sig", | ||
616 | &sig_enc_data, | ||
617 | &sig_enc_length)) | ||
618 | { | ||
619 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
620 | GNUNET_free (public_enc_data); | ||
621 | GNUNET_free (secret_enc_data); | ||
622 | GNUNET_break (0); | ||
623 | return GNUNET_SYSERR; | ||
624 | } | ||
625 | |||
626 | pkey = GNUNET_CRYPTO_rsa_public_key_decode (public_enc_data, | ||
627 | public_enc_len); | ||
628 | GNUNET_assert (NULL != pkey); | ||
629 | skey = GNUNET_CRYPTO_rsa_private_key_decode (secret_enc_data, | ||
630 | secret_enc_len); | ||
631 | GNUNET_assert (NULL != skey); | ||
632 | |||
633 | GNUNET_assert (GNUNET_YES == | ||
634 | GNUNET_CRYPTO_rsa_blind (&message_hash, | ||
635 | sizeof (message_hash), | ||
636 | &bks, | ||
637 | pkey, | ||
638 | &bm_comp)); | ||
639 | if ( (bm.blinded_msg_size != | ||
640 | bm_comp.blinded_msg_size) || | ||
641 | (0 != memcmp (bm.blinded_msg, | ||
642 | bm_comp.blinded_msg, | ||
643 | bm.blinded_msg_size)) ) | ||
644 | { | ||
645 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
646 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm_comp); | ||
647 | GNUNET_free (public_enc_data); | ||
648 | GNUNET_free (secret_enc_data); | ||
649 | GNUNET_free (sig_enc_data); | ||
650 | GNUNET_CRYPTO_rsa_private_key_free (skey); | ||
651 | GNUNET_CRYPTO_rsa_public_key_free (pkey); | ||
652 | GNUNET_break (0); | ||
653 | return GNUNET_NO; | ||
654 | } | ||
655 | blinded_sig = GNUNET_CRYPTO_rsa_sign_blinded (skey, | ||
656 | &bm); | ||
657 | sig = GNUNET_CRYPTO_rsa_unblind (blinded_sig, | ||
658 | &bks, | ||
659 | pkey); | ||
660 | GNUNET_assert (GNUNET_YES == | ||
661 | GNUNET_CRYPTO_rsa_verify (&message_hash, | ||
662 | sizeof (message_hash), | ||
663 | sig, | ||
664 | pkey)); | ||
665 | GNUNET_free (public_enc_data); | ||
666 | public_enc_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, | ||
667 | &public_enc_data); | ||
668 | sig_enc_length_comp = GNUNET_CRYPTO_rsa_signature_encode (sig, | ||
669 | &sig_enc_data_comp); | ||
670 | |||
671 | if ( (sig_enc_length != sig_enc_length_comp) || | ||
672 | (0 != memcmp (sig_enc_data, sig_enc_data_comp, sig_enc_length) )) | ||
673 | { | ||
674 | GNUNET_CRYPTO_rsa_signature_free (blinded_sig); | ||
675 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
676 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm_comp); | ||
677 | GNUNET_free (public_enc_data); | ||
678 | GNUNET_free (secret_enc_data); | ||
679 | GNUNET_free (sig_enc_data); | ||
680 | GNUNET_free (sig_enc_data_comp); | ||
681 | GNUNET_CRYPTO_rsa_private_key_free (skey); | ||
682 | GNUNET_CRYPTO_rsa_signature_free (sig); | ||
683 | GNUNET_CRYPTO_rsa_public_key_free (pkey); | ||
684 | GNUNET_break (0); | ||
685 | return GNUNET_NO; | ||
686 | } | ||
687 | GNUNET_CRYPTO_rsa_signature_free (blinded_sig); | ||
688 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
689 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm_comp); | ||
690 | GNUNET_free (public_enc_data); | ||
691 | GNUNET_free (secret_enc_data); | ||
692 | GNUNET_free (sig_enc_data); | ||
693 | GNUNET_free (sig_enc_data_comp); | ||
694 | GNUNET_CRYPTO_rsa_signature_free (sig); | ||
695 | GNUNET_CRYPTO_rsa_public_key_free (pkey); | ||
696 | GNUNET_CRYPTO_rsa_private_key_free (skey); | ||
697 | } | ||
698 | else if (0 == strcmp (operation, "cs_blind_signing")) | ||
699 | { | ||
700 | struct GNUNET_CRYPTO_CsPrivateKey priv; | ||
701 | struct GNUNET_CRYPTO_CsPublicKey pub; | ||
702 | struct GNUNET_CRYPTO_CsBlindingSecret bs[2]; | ||
703 | struct GNUNET_CRYPTO_CsRSecret r_priv[2]; | ||
704 | struct GNUNET_CRYPTO_CsRPublic r_pub[2]; | ||
705 | struct GNUNET_CRYPTO_CSPublicRPairP r_pub_blind; | ||
706 | struct GNUNET_CRYPTO_CsC c[2]; | ||
707 | struct GNUNET_CRYPTO_CsS signature_scalar; | ||
708 | struct GNUNET_CRYPTO_CsBlindS blinded_s; | ||
709 | struct GNUNET_CRYPTO_CsSignature sig; | ||
710 | struct GNUNET_CRYPTO_CsSessionNonce snonce; | ||
711 | struct GNUNET_CRYPTO_CsBlindingNonce bnonce; | ||
712 | struct GNUNET_HashCode message_hash; | ||
713 | unsigned int b; | ||
714 | |||
715 | if (GNUNET_OK != expect_data_fixed (vec, | ||
716 | "message_hash", | ||
717 | &message_hash, | ||
718 | sizeof (message_hash))) | ||
719 | { | ||
720 | GNUNET_break (0); | ||
721 | return GNUNET_SYSERR; | ||
722 | } | ||
723 | if (GNUNET_OK != expect_data_fixed (vec, | ||
724 | "cs_public_key", | ||
725 | &pub, | ||
726 | sizeof (pub))) | ||
727 | { | ||
728 | GNUNET_break (0); | ||
729 | return GNUNET_SYSERR; | ||
730 | } | ||
731 | |||
732 | if (GNUNET_OK != | ||
733 | expect_data_fixed (vec, | ||
734 | "cs_private_key", | ||
735 | &priv, | ||
736 | sizeof (priv))) | ||
737 | { | ||
738 | GNUNET_break (0); | ||
739 | return GNUNET_SYSERR; | ||
740 | } | ||
741 | if (GNUNET_OK != | ||
742 | expect_data_fixed (vec, | ||
743 | "cs_nonce", | ||
744 | &snonce, | ||
745 | sizeof (snonce))) | ||
746 | { | ||
747 | GNUNET_break (0); | ||
748 | return GNUNET_SYSERR; | ||
749 | } | ||
750 | /* historically, the tvg used the same nonce for | ||
751 | both, which is HORRIBLE for production, but | ||
752 | maybe OK for TVG... */ | ||
753 | memcpy (&bnonce, | ||
754 | &snonce, | ||
755 | sizeof (snonce)); | ||
756 | if (GNUNET_OK != | ||
757 | expect_data_fixed (vec, | ||
758 | "cs_r_priv_0", | ||
759 | &r_priv[0], | ||
760 | sizeof (r_priv[0]))) | ||
761 | { | ||
762 | GNUNET_break (0); | ||
763 | return GNUNET_SYSERR; | ||
764 | } | ||
765 | if (GNUNET_OK != expect_data_fixed (vec, | ||
766 | "cs_r_priv_1", | ||
767 | &r_priv[1], | ||
768 | sizeof (r_priv[1]))) | ||
769 | { | ||
770 | GNUNET_break (0); | ||
771 | return GNUNET_SYSERR; | ||
772 | } | ||
773 | if (GNUNET_OK != expect_data_fixed (vec, | ||
774 | "cs_r_pub_0", | ||
775 | &r_pub[0], | ||
776 | sizeof (r_pub[0]))) | ||
777 | { | ||
778 | GNUNET_break (0); | ||
779 | return GNUNET_SYSERR; | ||
780 | } | ||
781 | if (GNUNET_OK != expect_data_fixed (vec, | ||
782 | "cs_r_pub_1", | ||
783 | &r_pub[1], | ||
784 | sizeof (r_pub[1]))) | ||
785 | { | ||
786 | GNUNET_break (0); | ||
787 | return GNUNET_SYSERR; | ||
788 | } | ||
789 | |||
790 | if (GNUNET_OK != expect_data_fixed (vec, | ||
791 | "cs_bs_alpha_0", | ||
792 | &bs[0].alpha, | ||
793 | sizeof (bs[0].alpha))) | ||
794 | { | ||
795 | GNUNET_break (0); | ||
796 | return GNUNET_SYSERR; | ||
797 | } | ||
798 | if (GNUNET_OK != expect_data_fixed (vec, | ||
799 | "cs_bs_alpha_1", | ||
800 | &bs[1].alpha, | ||
801 | sizeof (bs[1].alpha))) | ||
802 | { | ||
803 | GNUNET_break (0); | ||
804 | return GNUNET_SYSERR; | ||
805 | } | ||
806 | if (GNUNET_OK != expect_data_fixed (vec, | ||
807 | "cs_bs_beta_0", | ||
808 | &bs[0].beta, | ||
809 | sizeof (bs[0].beta))) | ||
810 | { | ||
811 | GNUNET_break (0); | ||
812 | return GNUNET_SYSERR; | ||
813 | } | ||
814 | if (GNUNET_OK != | ||
815 | expect_data_fixed (vec, | ||
816 | "cs_bs_beta_1", | ||
817 | &bs[1].beta, | ||
818 | sizeof (bs[1].beta))) | ||
819 | { | ||
820 | GNUNET_break (0); | ||
821 | return GNUNET_SYSERR; | ||
822 | } | ||
823 | if (GNUNET_OK != | ||
824 | expect_data_fixed (vec, | ||
825 | "cs_r_pub_blind_0", | ||
826 | &r_pub_blind.r_pub[0], | ||
827 | sizeof (r_pub_blind.r_pub[0]))) | ||
828 | { | ||
829 | GNUNET_break (0); | ||
830 | return GNUNET_SYSERR; | ||
831 | } | ||
832 | if (GNUNET_OK != | ||
833 | expect_data_fixed (vec, | ||
834 | "cs_r_pub_blind_1", | ||
835 | &r_pub_blind.r_pub[1], | ||
836 | sizeof (r_pub_blind.r_pub[1]))) | ||
837 | { | ||
838 | GNUNET_break (0); | ||
839 | return GNUNET_SYSERR; | ||
840 | } | ||
841 | if (GNUNET_OK != | ||
842 | expect_data_fixed (vec, | ||
843 | "cs_c_0", | ||
844 | &c[0], | ||
845 | sizeof (c[0]))) | ||
846 | { | ||
847 | GNUNET_break (0); | ||
848 | return GNUNET_SYSERR; | ||
849 | } | ||
850 | if (GNUNET_OK != expect_data_fixed (vec, | ||
851 | "cs_c_1", | ||
852 | &c[1], | ||
853 | sizeof (c[1]))) | ||
854 | { | ||
855 | GNUNET_break (0); | ||
856 | return GNUNET_SYSERR; | ||
857 | } | ||
858 | if (GNUNET_OK != expect_data_fixed (vec, | ||
859 | "cs_blind_s", | ||
860 | &blinded_s, | ||
861 | sizeof (blinded_s))) | ||
862 | { | ||
863 | GNUNET_break (0); | ||
864 | return GNUNET_SYSERR; | ||
865 | } | ||
866 | if (GNUNET_OK != expect_data_fixed (vec, | ||
867 | "cs_b", | ||
868 | &b, | ||
869 | sizeof (b))) | ||
870 | { | ||
871 | GNUNET_break (0); | ||
872 | return GNUNET_SYSERR; | ||
873 | } | ||
874 | if (GNUNET_OK != expect_data_fixed (vec, | ||
875 | "cs_sig_s", | ||
876 | &signature_scalar, | ||
877 | sizeof (signature_scalar))) | ||
878 | { | ||
879 | GNUNET_break (0); | ||
880 | return GNUNET_SYSERR; | ||
881 | } | ||
882 | sig.s_scalar = signature_scalar; | ||
883 | if (GNUNET_OK != expect_data_fixed (vec, | ||
884 | "cs_sig_R", | ||
885 | &sig.r_point, | ||
886 | sizeof (sig.r_point))) | ||
887 | { | ||
888 | GNUNET_break (0); | ||
889 | return GNUNET_SYSERR; | ||
890 | } | ||
891 | |||
892 | if ((b != 1) && (b != 0)) | ||
893 | { | ||
894 | GNUNET_break (0); | ||
895 | return GNUNET_SYSERR; | ||
896 | } | ||
897 | |||
898 | struct GNUNET_CRYPTO_CsRSecret r_priv_comp[2]; | ||
899 | struct GNUNET_CRYPTO_CsRPublic r_pub_comp[2]; | ||
900 | struct GNUNET_CRYPTO_CsBlindingSecret bs_comp[2]; | ||
901 | struct GNUNET_CRYPTO_CsC c_comp[2]; | ||
902 | struct GNUNET_CRYPTO_CSPublicRPairP r_pub_blind_comp; | ||
903 | struct GNUNET_CRYPTO_CsBlindSignature blinded_s_comp; | ||
904 | struct GNUNET_CRYPTO_CsS signature_scalar_comp; | ||
905 | struct GNUNET_CRYPTO_CsSignature sig_comp; | ||
906 | |||
907 | GNUNET_CRYPTO_cs_r_derive (&snonce, | ||
908 | "rw", | ||
909 | &priv, | ||
910 | r_priv_comp); | ||
911 | GNUNET_CRYPTO_cs_r_get_public (&r_priv_comp[0], | ||
912 | &r_pub_comp[0]); | ||
913 | GNUNET_CRYPTO_cs_r_get_public (&r_priv_comp[1], | ||
914 | &r_pub_comp[1]); | ||
915 | GNUNET_assert (0 == memcmp (&r_priv_comp, | ||
916 | &r_priv, | ||
917 | sizeof(struct GNUNET_CRYPTO_CsRSecret) * 2)); | ||
918 | GNUNET_assert (0 == memcmp (&r_pub_comp, | ||
919 | &r_pub, | ||
920 | sizeof(struct GNUNET_CRYPTO_CsRPublic) * 2)); | ||
921 | |||
922 | GNUNET_CRYPTO_cs_blinding_secrets_derive (&bnonce, | ||
923 | bs_comp); | ||
924 | GNUNET_assert (0 == | ||
925 | memcmp (&bs_comp, | ||
926 | &bs, | ||
927 | sizeof(struct GNUNET_CRYPTO_CsBlindingSecret) | ||
928 | * 2)); | ||
929 | GNUNET_CRYPTO_cs_calc_blinded_c (bs_comp, | ||
930 | r_pub_comp, | ||
931 | &pub, | ||
932 | &message_hash, | ||
933 | sizeof(message_hash), | ||
934 | c_comp, | ||
935 | &r_pub_blind_comp); | ||
936 | GNUNET_assert (0 == | ||
937 | memcmp (&c_comp, | ||
938 | &c, | ||
939 | sizeof(struct GNUNET_CRYPTO_CsC) * 2)); | ||
940 | GNUNET_assert (0 == | ||
941 | GNUNET_memcmp (&r_pub_blind_comp, | ||
942 | &r_pub_blind)); | ||
943 | { | ||
944 | struct GNUNET_CRYPTO_CsBlindedMessage bm = { | ||
945 | .c[0] = c_comp[0], | ||
946 | .c[1] = c_comp[1], | ||
947 | .nonce = snonce | ||
948 | }; | ||
949 | |||
950 | GNUNET_CRYPTO_cs_sign_derive (&priv, | ||
951 | r_priv_comp, | ||
952 | &bm, | ||
953 | &blinded_s_comp); | ||
954 | } | ||
955 | GNUNET_assert (0 == | ||
956 | GNUNET_memcmp (&blinded_s_comp.s_scalar, | ||
957 | &blinded_s)); | ||
958 | GNUNET_assert (b == blinded_s_comp.b); | ||
959 | GNUNET_CRYPTO_cs_unblind (&blinded_s_comp.s_scalar, | ||
960 | &bs_comp[b], | ||
961 | &signature_scalar_comp); | ||
962 | GNUNET_assert (0 == | ||
963 | GNUNET_memcmp (&signature_scalar_comp, | ||
964 | &signature_scalar)); | ||
965 | sig_comp.r_point = r_pub_blind_comp.r_pub[b]; | ||
966 | sig_comp.s_scalar = signature_scalar_comp; | ||
967 | GNUNET_assert (0 == memcmp (&sig_comp, | ||
968 | &sig, | ||
969 | sizeof(sig_comp))); | ||
970 | if (GNUNET_OK != | ||
971 | GNUNET_CRYPTO_cs_verify (&sig_comp, | ||
972 | &pub, | ||
973 | &message_hash, | ||
974 | sizeof(message_hash))) | ||
975 | { | ||
976 | GNUNET_break (0); | ||
977 | return GNUNET_SYSERR; | ||
978 | } | ||
979 | } | ||
980 | else | ||
981 | { | ||
982 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
983 | "unsupported operation '%s'\n", operation); | ||
984 | } | ||
985 | |||
986 | return GNUNET_OK; | ||
987 | } | ||
988 | |||
989 | |||
990 | /** | ||
991 | * Check test vectors from stdin. | ||
992 | * | ||
993 | * @returns global exit code | ||
994 | */ | ||
995 | static int | ||
996 | check_vectors () | ||
997 | { | ||
998 | json_error_t err; | ||
999 | json_t *vecfile = json_loadf (stdin, 0, &err); | ||
1000 | const char *encoding; | ||
1001 | json_t *vectors; | ||
1002 | |||
1003 | if (NULL == vecfile) | ||
1004 | { | ||
1005 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unable to parse JSON\n"); | ||
1006 | return 1; | ||
1007 | } | ||
1008 | encoding = json_string_value (json_object_get (vecfile, | ||
1009 | "encoding")); | ||
1010 | if ( (NULL == encoding) || (0 != strcmp (encoding, "base32crockford")) ) | ||
1011 | { | ||
1012 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unsupported or missing encoding\n"); | ||
1013 | json_decref (vecfile); | ||
1014 | return 1; | ||
1015 | } | ||
1016 | vectors = json_object_get (vecfile, "vectors"); | ||
1017 | if (! json_is_array (vectors)) | ||
1018 | { | ||
1019 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "bad vectors\n"); | ||
1020 | json_decref (vecfile); | ||
1021 | return 1; | ||
1022 | } | ||
1023 | { | ||
1024 | /* array is a JSON array */ | ||
1025 | size_t index; | ||
1026 | json_t *value; | ||
1027 | enum GNUNET_GenericReturnValue ret = GNUNET_OK; | ||
1028 | |||
1029 | json_array_foreach (vectors, index, value) { | ||
1030 | const char *op = json_string_value (json_object_get (value, | ||
1031 | "operation")); | ||
1032 | |||
1033 | if (NULL == op) | ||
1034 | { | ||
1035 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1036 | "missing operation\n"); | ||
1037 | ret = GNUNET_SYSERR; | ||
1038 | break; | ||
1039 | } | ||
1040 | ret = checkvec (op, value); | ||
1041 | if (GNUNET_OK != ret) | ||
1042 | { | ||
1043 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1044 | "bad vector %u\n", | ||
1045 | (unsigned int) index); | ||
1046 | break; | ||
1047 | } | ||
1048 | } | ||
1049 | json_decref (vecfile); | ||
1050 | return (ret == GNUNET_OK) ? 0 : 1; | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | |||
1055 | /** | ||
1056 | * Output test vectors. | ||
1057 | * | ||
1058 | * @returns global exit code | ||
1059 | */ | ||
1060 | static int | ||
1061 | output_vectors () | ||
1062 | { | ||
1063 | json_t *vecfile = json_object (); | ||
1064 | json_t *vecs = json_array (); | ||
1065 | |||
1066 | json_object_set_new (vecfile, | ||
1067 | "encoding", | ||
1068 | json_string ("base32crockford")); | ||
1069 | json_object_set_new (vecfile, | ||
1070 | "producer", | ||
1071 | json_string ("GNUnet " PACKAGE_VERSION " " VCS_VERSION)); | ||
1072 | json_object_set_new (vecfile, | ||
1073 | "vectors", | ||
1074 | vecs); | ||
1075 | |||
1076 | { | ||
1077 | json_t *vec = vec_for (vecs, "hash"); | ||
1078 | struct GNUNET_HashCode hc; | ||
1079 | char *str = "Hello, GNUnet"; | ||
1080 | |||
1081 | GNUNET_CRYPTO_hash (str, strlen (str), &hc); | ||
1082 | |||
1083 | d2j (vec, "input", str, strlen (str)); | ||
1084 | d2j (vec, "output", &hc, sizeof (struct GNUNET_HashCode)); | ||
1085 | } | ||
1086 | { | ||
1087 | json_t *vec = vec_for (vecs, "ecc_ecdh"); | ||
1088 | struct GNUNET_CRYPTO_EcdhePrivateKey priv1; | ||
1089 | struct GNUNET_CRYPTO_EcdhePublicKey pub1; | ||
1090 | struct GNUNET_CRYPTO_EcdhePrivateKey priv2; | ||
1091 | struct GNUNET_HashCode skm; | ||
1092 | |||
1093 | GNUNET_CRYPTO_ecdhe_key_create (&priv1); | ||
1094 | GNUNET_CRYPTO_ecdhe_key_create (&priv2); | ||
1095 | GNUNET_CRYPTO_ecdhe_key_get_public (&priv1, | ||
1096 | &pub1); | ||
1097 | GNUNET_assert (GNUNET_OK == | ||
1098 | GNUNET_CRYPTO_ecc_ecdh (&priv2, | ||
1099 | &pub1, | ||
1100 | &skm)); | ||
1101 | |||
1102 | d2j (vec, | ||
1103 | "priv1", | ||
1104 | &priv1, | ||
1105 | sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); | ||
1106 | d2j (vec, | ||
1107 | "pub1", | ||
1108 | &pub1, | ||
1109 | sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)); | ||
1110 | d2j (vec, | ||
1111 | "priv2", | ||
1112 | &priv2, | ||
1113 | sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); | ||
1114 | d2j (vec, | ||
1115 | "skm", | ||
1116 | &skm, | ||
1117 | sizeof (struct GNUNET_HashCode)); | ||
1118 | } | ||
1119 | |||
1120 | { | ||
1121 | json_t *vec = vec_for (vecs, "eddsa_key_derivation"); | ||
1122 | struct GNUNET_CRYPTO_EddsaPrivateKey priv; | ||
1123 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
1124 | |||
1125 | GNUNET_CRYPTO_eddsa_key_create (&priv); | ||
1126 | GNUNET_CRYPTO_eddsa_key_get_public (&priv, | ||
1127 | &pub); | ||
1128 | |||
1129 | d2j (vec, | ||
1130 | "priv", | ||
1131 | &priv, | ||
1132 | sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); | ||
1133 | d2j (vec, | ||
1134 | "pub", | ||
1135 | &pub, | ||
1136 | sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); | ||
1137 | } | ||
1138 | { | ||
1139 | json_t *vec = vec_for (vecs, "eddsa_signing"); | ||
1140 | struct GNUNET_CRYPTO_EddsaPrivateKey priv; | ||
1141 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
1142 | struct GNUNET_CRYPTO_EddsaSignature sig; | ||
1143 | struct TestSignatureDataPS data = { 0 }; | ||
1144 | |||
1145 | GNUNET_CRYPTO_eddsa_key_create (&priv); | ||
1146 | GNUNET_CRYPTO_eddsa_key_get_public (&priv, | ||
1147 | &pub); | ||
1148 | data.purpose.size = htonl (sizeof (data)); | ||
1149 | data.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST); | ||
1150 | GNUNET_CRYPTO_eddsa_sign (&priv, | ||
1151 | &data, | ||
1152 | &sig); | ||
1153 | GNUNET_assert (GNUNET_OK == | ||
1154 | GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST, | ||
1155 | &data, | ||
1156 | &sig, | ||
1157 | &pub)); | ||
1158 | |||
1159 | d2j (vec, | ||
1160 | "priv", | ||
1161 | &priv, | ||
1162 | sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); | ||
1163 | d2j (vec, | ||
1164 | "pub", | ||
1165 | &pub, | ||
1166 | sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); | ||
1167 | d2j (vec, | ||
1168 | "data", | ||
1169 | &data, | ||
1170 | sizeof (struct TestSignatureDataPS)); | ||
1171 | d2j (vec, | ||
1172 | "sig", | ||
1173 | &sig, | ||
1174 | sizeof (struct GNUNET_CRYPTO_EddsaSignature)); | ||
1175 | } | ||
1176 | |||
1177 | { | ||
1178 | json_t *vec = vec_for (vecs, "kdf"); | ||
1179 | size_t out_len = 64; | ||
1180 | char out[out_len]; | ||
1181 | char *ikm = "I'm the secret input key material"; | ||
1182 | char *salt = "I'm very salty"; | ||
1183 | char *ctx = "I'm a context chunk, also known as 'info' in the RFC"; | ||
1184 | |||
1185 | GNUNET_assert (GNUNET_OK == | ||
1186 | GNUNET_CRYPTO_kdf (&out, | ||
1187 | out_len, | ||
1188 | salt, | ||
1189 | strlen (salt), | ||
1190 | ikm, | ||
1191 | strlen (ikm), | ||
1192 | ctx, | ||
1193 | strlen (ctx), | ||
1194 | NULL)); | ||
1195 | |||
1196 | d2j (vec, | ||
1197 | "salt", | ||
1198 | salt, | ||
1199 | strlen (salt)); | ||
1200 | d2j (vec, | ||
1201 | "ikm", | ||
1202 | ikm, | ||
1203 | strlen (ikm)); | ||
1204 | d2j (vec, | ||
1205 | "ctx", | ||
1206 | ctx, | ||
1207 | strlen (ctx)); | ||
1208 | uint2j (vec, | ||
1209 | "out_len", | ||
1210 | (unsigned int) out_len); | ||
1211 | d2j (vec, | ||
1212 | "out", | ||
1213 | out, | ||
1214 | out_len); | ||
1215 | } | ||
1216 | { | ||
1217 | json_t *vec = vec_for (vecs, "eddsa_ecdh"); | ||
1218 | struct GNUNET_CRYPTO_EcdhePrivateKey priv_ecdhe; | ||
1219 | struct GNUNET_CRYPTO_EcdhePublicKey pub_ecdhe; | ||
1220 | struct GNUNET_CRYPTO_EddsaPrivateKey priv_eddsa; | ||
1221 | struct GNUNET_CRYPTO_EddsaPublicKey pub_eddsa; | ||
1222 | struct GNUNET_HashCode key_material; | ||
1223 | |||
1224 | GNUNET_CRYPTO_ecdhe_key_create (&priv_ecdhe); | ||
1225 | GNUNET_CRYPTO_ecdhe_key_get_public (&priv_ecdhe, &pub_ecdhe); | ||
1226 | GNUNET_CRYPTO_eddsa_key_create (&priv_eddsa); | ||
1227 | GNUNET_CRYPTO_eddsa_key_get_public (&priv_eddsa, &pub_eddsa); | ||
1228 | GNUNET_CRYPTO_ecdh_eddsa (&priv_ecdhe, &pub_eddsa, &key_material); | ||
1229 | |||
1230 | d2j (vec, "priv_ecdhe", | ||
1231 | &priv_ecdhe, | ||
1232 | sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); | ||
1233 | d2j (vec, "pub_ecdhe", | ||
1234 | &pub_ecdhe, | ||
1235 | sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)); | ||
1236 | d2j (vec, "priv_eddsa", | ||
1237 | &priv_eddsa, | ||
1238 | sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); | ||
1239 | d2j (vec, "pub_eddsa", | ||
1240 | &pub_eddsa, | ||
1241 | sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); | ||
1242 | d2j (vec, "key_material", | ||
1243 | &key_material, | ||
1244 | sizeof (struct GNUNET_HashCode)); | ||
1245 | } | ||
1246 | |||
1247 | { | ||
1248 | json_t *vec = vec_for (vecs, "edx25519_derive"); | ||
1249 | struct GNUNET_CRYPTO_Edx25519PrivateKey priv1_edx; | ||
1250 | struct GNUNET_CRYPTO_Edx25519PublicKey pub1_edx; | ||
1251 | struct GNUNET_CRYPTO_Edx25519PrivateKey priv2_edx; | ||
1252 | struct GNUNET_CRYPTO_Edx25519PublicKey pub2_edx; | ||
1253 | struct GNUNET_HashCode seed; | ||
1254 | |||
1255 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1256 | &seed, | ||
1257 | sizeof (struct GNUNET_HashCode)); | ||
1258 | GNUNET_CRYPTO_edx25519_key_create (&priv1_edx); | ||
1259 | GNUNET_CRYPTO_edx25519_key_get_public (&priv1_edx, &pub1_edx); | ||
1260 | GNUNET_CRYPTO_edx25519_private_key_derive (&priv1_edx, | ||
1261 | &seed, | ||
1262 | sizeof (seed), | ||
1263 | &priv2_edx); | ||
1264 | GNUNET_CRYPTO_edx25519_public_key_derive (&pub1_edx, | ||
1265 | &seed, | ||
1266 | sizeof (seed), | ||
1267 | &pub2_edx); | ||
1268 | |||
1269 | d2j (vec, "priv1_edx", | ||
1270 | &priv1_edx, | ||
1271 | sizeof (struct GNUNET_CRYPTO_Edx25519PrivateKey)); | ||
1272 | d2j (vec, "pub1_edx", | ||
1273 | &pub1_edx, | ||
1274 | sizeof (struct GNUNET_CRYPTO_Edx25519PublicKey)); | ||
1275 | d2j (vec, "seed", | ||
1276 | &seed, | ||
1277 | sizeof (struct GNUNET_HashCode)); | ||
1278 | d2j (vec, "priv2_edx", | ||
1279 | &priv2_edx, | ||
1280 | sizeof (struct GNUNET_CRYPTO_Edx25519PrivateKey)); | ||
1281 | d2j (vec, "pub2_edx", | ||
1282 | &pub2_edx, | ||
1283 | sizeof (struct GNUNET_CRYPTO_Edx25519PublicKey)); | ||
1284 | } | ||
1285 | |||
1286 | { | ||
1287 | json_t *vec = vec_for (vecs, "rsa_blind_signing"); | ||
1288 | |||
1289 | struct GNUNET_CRYPTO_RsaPrivateKey *skey; | ||
1290 | struct GNUNET_CRYPTO_RsaPublicKey *pkey; | ||
1291 | struct GNUNET_HashCode message_hash; | ||
1292 | struct GNUNET_CRYPTO_RsaBlindingKeySecret bks; | ||
1293 | struct GNUNET_CRYPTO_RsaSignature *blinded_sig; | ||
1294 | struct GNUNET_CRYPTO_RsaSignature *sig; | ||
1295 | struct GNUNET_CRYPTO_RsaBlindedMessage bm; | ||
1296 | void *public_enc_data; | ||
1297 | size_t public_enc_len; | ||
1298 | void *secret_enc_data; | ||
1299 | size_t secret_enc_len; | ||
1300 | void *blinded_sig_enc_data; | ||
1301 | size_t blinded_sig_enc_length; | ||
1302 | void *sig_enc_data; | ||
1303 | size_t sig_enc_length; | ||
1304 | |||
1305 | skey = GNUNET_CRYPTO_rsa_private_key_create (2048); | ||
1306 | pkey = GNUNET_CRYPTO_rsa_private_key_get_public (skey); | ||
1307 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1308 | &message_hash, | ||
1309 | sizeof (struct GNUNET_HashCode)); | ||
1310 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1311 | &bks, | ||
1312 | sizeof (struct | ||
1313 | GNUNET_CRYPTO_RsaBlindingKeySecret)); | ||
1314 | GNUNET_assert (GNUNET_YES == | ||
1315 | GNUNET_CRYPTO_rsa_blind (&message_hash, | ||
1316 | sizeof (message_hash), | ||
1317 | &bks, | ||
1318 | pkey, | ||
1319 | &bm)); | ||
1320 | blinded_sig = GNUNET_CRYPTO_rsa_sign_blinded (skey, | ||
1321 | &bm); | ||
1322 | sig = GNUNET_CRYPTO_rsa_unblind (blinded_sig, | ||
1323 | &bks, | ||
1324 | pkey); | ||
1325 | GNUNET_assert (GNUNET_YES == | ||
1326 | GNUNET_CRYPTO_rsa_verify (&message_hash, | ||
1327 | sizeof (message_hash), | ||
1328 | sig, | ||
1329 | pkey)); | ||
1330 | public_enc_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, | ||
1331 | &public_enc_data); | ||
1332 | secret_enc_len = GNUNET_CRYPTO_rsa_private_key_encode (skey, | ||
1333 | &secret_enc_data); | ||
1334 | blinded_sig_enc_length | ||
1335 | = GNUNET_CRYPTO_rsa_signature_encode (blinded_sig, | ||
1336 | &blinded_sig_enc_data); | ||
1337 | sig_enc_length = GNUNET_CRYPTO_rsa_signature_encode (sig, | ||
1338 | &sig_enc_data); | ||
1339 | d2j (vec, | ||
1340 | "message_hash", | ||
1341 | &message_hash, | ||
1342 | sizeof (struct GNUNET_HashCode)); | ||
1343 | d2j (vec, | ||
1344 | "rsa_public_key", | ||
1345 | public_enc_data, | ||
1346 | public_enc_len); | ||
1347 | d2j (vec, | ||
1348 | "rsa_private_key", | ||
1349 | secret_enc_data, | ||
1350 | secret_enc_len); | ||
1351 | d2j (vec, | ||
1352 | "blinding_key_secret", | ||
1353 | &bks, | ||
1354 | sizeof (struct GNUNET_CRYPTO_RsaBlindingKeySecret)); | ||
1355 | d2j (vec, | ||
1356 | "blinded_message", | ||
1357 | bm.blinded_msg, | ||
1358 | bm.blinded_msg_size); | ||
1359 | d2j (vec, | ||
1360 | "blinded_sig", | ||
1361 | blinded_sig_enc_data, | ||
1362 | blinded_sig_enc_length); | ||
1363 | d2j (vec, | ||
1364 | "sig", | ||
1365 | sig_enc_data, | ||
1366 | sig_enc_length); | ||
1367 | GNUNET_CRYPTO_rsa_private_key_free (skey); | ||
1368 | GNUNET_CRYPTO_rsa_public_key_free (pkey); | ||
1369 | GNUNET_CRYPTO_rsa_signature_free (sig); | ||
1370 | GNUNET_CRYPTO_rsa_signature_free (blinded_sig); | ||
1371 | GNUNET_free (public_enc_data); | ||
1372 | GNUNET_CRYPTO_rsa_blinded_message_free (&bm); | ||
1373 | GNUNET_free (sig_enc_data); | ||
1374 | GNUNET_free (blinded_sig_enc_data); | ||
1375 | GNUNET_free (secret_enc_data); | ||
1376 | } | ||
1377 | |||
1378 | { | ||
1379 | json_t *vec = vec_for (vecs, "cs_blind_signing"); | ||
1380 | |||
1381 | struct GNUNET_CRYPTO_CsPrivateKey priv; | ||
1382 | struct GNUNET_CRYPTO_CsPublicKey pub; | ||
1383 | struct GNUNET_CRYPTO_CsBlindingSecret bs[2]; | ||
1384 | struct GNUNET_CRYPTO_CsRSecret r_priv[2]; | ||
1385 | struct GNUNET_CRYPTO_CsRPublic r_pub[2]; | ||
1386 | struct GNUNET_CRYPTO_CSPublicRPairP r_pub_blind; | ||
1387 | struct GNUNET_CRYPTO_CsC c[2]; | ||
1388 | struct GNUNET_CRYPTO_CsS signature_scalar; | ||
1389 | struct GNUNET_CRYPTO_CsBlindSignature blinded_s; | ||
1390 | struct GNUNET_CRYPTO_CsSignature sig; | ||
1391 | struct GNUNET_CRYPTO_CsSessionNonce snonce; | ||
1392 | struct GNUNET_CRYPTO_CsBlindingNonce bnonce; | ||
1393 | struct GNUNET_HashCode message_hash; | ||
1394 | |||
1395 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1396 | &message_hash, | ||
1397 | sizeof (struct GNUNET_HashCode)); | ||
1398 | |||
1399 | GNUNET_CRYPTO_cs_private_key_generate (&priv); | ||
1400 | GNUNET_CRYPTO_cs_private_key_get_public (&priv, | ||
1401 | &pub); | ||
1402 | GNUNET_assert (GNUNET_YES == | ||
1403 | GNUNET_CRYPTO_hkdf (&snonce, | ||
1404 | sizeof(snonce), | ||
1405 | GCRY_MD_SHA512, | ||
1406 | GCRY_MD_SHA256, | ||
1407 | "nonce", | ||
1408 | strlen ("nonce"), | ||
1409 | "nonce_secret", | ||
1410 | strlen ("nonce_secret"), | ||
1411 | NULL, | ||
1412 | 0)); | ||
1413 | /* NOTE: historically, we made the bad choice of | ||
1414 | making both nonces the same. Maybe barely OK | ||
1415 | for the TGV, not good for production! */ | ||
1416 | memcpy (&bnonce, | ||
1417 | &snonce, | ||
1418 | sizeof (snonce)); | ||
1419 | GNUNET_CRYPTO_cs_r_derive (&snonce, | ||
1420 | "rw", | ||
1421 | &priv, | ||
1422 | r_priv); | ||
1423 | GNUNET_CRYPTO_cs_r_get_public (&r_priv[0], | ||
1424 | &r_pub[0]); | ||
1425 | GNUNET_CRYPTO_cs_r_get_public (&r_priv[1], | ||
1426 | &r_pub[1]); | ||
1427 | GNUNET_CRYPTO_cs_blinding_secrets_derive (&bnonce, | ||
1428 | bs); | ||
1429 | GNUNET_CRYPTO_cs_calc_blinded_c (bs, | ||
1430 | r_pub, | ||
1431 | &pub, | ||
1432 | &message_hash, | ||
1433 | sizeof(message_hash), | ||
1434 | c, | ||
1435 | &r_pub_blind); | ||
1436 | { | ||
1437 | struct GNUNET_CRYPTO_CsBlindedMessage bm = { | ||
1438 | .c[0] = c[0], | ||
1439 | .c[1] = c[1], | ||
1440 | .nonce = snonce | ||
1441 | }; | ||
1442 | |||
1443 | GNUNET_CRYPTO_cs_sign_derive (&priv, | ||
1444 | r_priv, | ||
1445 | &bm, | ||
1446 | &blinded_s); | ||
1447 | } | ||
1448 | GNUNET_CRYPTO_cs_unblind (&blinded_s.s_scalar, | ||
1449 | &bs[blinded_s.b], | ||
1450 | &signature_scalar); | ||
1451 | sig.r_point = r_pub_blind.r_pub[blinded_s.b]; | ||
1452 | sig.s_scalar = signature_scalar; | ||
1453 | if (GNUNET_OK != | ||
1454 | GNUNET_CRYPTO_cs_verify (&sig, | ||
1455 | &pub, | ||
1456 | &message_hash, | ||
1457 | sizeof(message_hash))) | ||
1458 | { | ||
1459 | GNUNET_break (0); | ||
1460 | return GNUNET_SYSERR; | ||
1461 | } | ||
1462 | d2j (vec, | ||
1463 | "message_hash", | ||
1464 | &message_hash, | ||
1465 | sizeof (struct GNUNET_HashCode)); | ||
1466 | d2j (vec, | ||
1467 | "cs_public_key", | ||
1468 | &pub, | ||
1469 | sizeof(pub)); | ||
1470 | d2j (vec, | ||
1471 | "cs_private_key", | ||
1472 | &priv, | ||
1473 | sizeof(priv)); | ||
1474 | d2j (vec, | ||
1475 | "cs_nonce", | ||
1476 | &snonce, | ||
1477 | sizeof(snonce)); | ||
1478 | d2j (vec, | ||
1479 | "cs_r_priv_0", | ||
1480 | &r_priv[0], | ||
1481 | sizeof(r_priv[0])); | ||
1482 | d2j (vec, | ||
1483 | "cs_r_priv_1", | ||
1484 | &r_priv[1], | ||
1485 | sizeof(r_priv[1])); | ||
1486 | d2j (vec, | ||
1487 | "cs_r_pub_0", | ||
1488 | &r_pub[0], | ||
1489 | sizeof(r_pub[0])); | ||
1490 | d2j (vec, | ||
1491 | "cs_r_pub_1", | ||
1492 | &r_pub[1], | ||
1493 | sizeof(r_pub[1])); | ||
1494 | d2j (vec, | ||
1495 | "cs_bs_alpha_0", | ||
1496 | &bs[0].alpha, | ||
1497 | sizeof(bs[0].alpha)); | ||
1498 | d2j (vec, | ||
1499 | "cs_bs_alpha_1", | ||
1500 | &bs[1].alpha, | ||
1501 | sizeof(bs[1].alpha)); | ||
1502 | d2j (vec, | ||
1503 | "cs_bs_beta_0", | ||
1504 | &bs[0].beta, | ||
1505 | sizeof(bs[0].beta)); | ||
1506 | d2j (vec, | ||
1507 | "cs_bs_beta_1", | ||
1508 | &bs[1].beta, | ||
1509 | sizeof(bs[1].beta)); | ||
1510 | d2j (vec, | ||
1511 | "cs_r_pub_blind_0", | ||
1512 | &r_pub_blind.r_pub[0], | ||
1513 | sizeof(r_pub_blind.r_pub[0])); | ||
1514 | d2j (vec, | ||
1515 | "cs_r_pub_blind_1", | ||
1516 | &r_pub_blind.r_pub[1], | ||
1517 | sizeof(r_pub_blind.r_pub[1])); | ||
1518 | d2j (vec, | ||
1519 | "cs_c_0", | ||
1520 | &c[0], | ||
1521 | sizeof(c[0])); | ||
1522 | d2j (vec, | ||
1523 | "cs_c_1", | ||
1524 | &c[1], | ||
1525 | sizeof(c[1])); | ||
1526 | d2j (vec, | ||
1527 | "cs_blind_s", | ||
1528 | &blinded_s, | ||
1529 | sizeof(blinded_s)); | ||
1530 | d2j (vec, | ||
1531 | "cs_b", | ||
1532 | &blinded_s.b, | ||
1533 | sizeof(blinded_s.b)); | ||
1534 | d2j (vec, | ||
1535 | "cs_sig_s", | ||
1536 | &signature_scalar, | ||
1537 | sizeof(signature_scalar)); | ||
1538 | d2j (vec, | ||
1539 | "cs_sig_R", | ||
1540 | &r_pub_blind.r_pub[blinded_s.b], | ||
1541 | sizeof(r_pub_blind.r_pub[blinded_s.b])); | ||
1542 | } | ||
1543 | |||
1544 | json_dumpf (vecfile, stdout, JSON_INDENT (2)); | ||
1545 | json_decref (vecfile); | ||
1546 | printf ("\n"); | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | |||
1552 | /** | ||
1553 | * Main function that will be run. | ||
1554 | * | ||
1555 | * @param cls closure | ||
1556 | * @param args remaining command-line arguments | ||
1557 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
1558 | * @param cfg configuration | ||
1559 | */ | ||
1560 | static void | ||
1561 | run (void *cls, | ||
1562 | char *const *args, | ||
1563 | const char *cfgfile, | ||
1564 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
1565 | { | ||
1566 | if (GNUNET_YES == verify_flag) | ||
1567 | global_ret = check_vectors (); | ||
1568 | else | ||
1569 | global_ret = output_vectors (); | ||
1570 | } | ||
1571 | |||
1572 | |||
1573 | /** | ||
1574 | * The main function of the test vector generation tool. | ||
1575 | * | ||
1576 | * @param argc number of arguments from the command line | ||
1577 | * @param argv command line arguments | ||
1578 | * @return 0 ok, 1 on error | ||
1579 | */ | ||
1580 | int | ||
1581 | main (int argc, | ||
1582 | char *const *argv) | ||
1583 | { | ||
1584 | const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
1585 | GNUNET_GETOPT_option_flag ('V', | ||
1586 | "verify", | ||
1587 | gettext_noop ( | ||
1588 | "verify a test vector from stdin"), | ||
1589 | &verify_flag), | ||
1590 | GNUNET_GETOPT_OPTION_END | ||
1591 | }; | ||
1592 | |||
1593 | GNUNET_assert (GNUNET_OK == | ||
1594 | GNUNET_log_setup ("gnunet-crypto-tvg", | ||
1595 | "INFO", | ||
1596 | NULL)); | ||
1597 | if (GNUNET_OK != | ||
1598 | GNUNET_PROGRAM_run (argc, argv, | ||
1599 | "gnunet-crypto-tvg", | ||
1600 | "Generate test vectors for cryptographic operations", | ||
1601 | options, | ||
1602 | &run, NULL)) | ||
1603 | return 1; | ||
1604 | return global_ret; | ||
1605 | } | ||
1606 | |||
1607 | |||
1608 | /* end of gnunet-crypto-tvg.c */ | ||
diff --git a/src/cli/util/gnunet-ecc.c b/src/cli/util/gnunet-ecc.c new file mode 100644 index 000000000..812745085 --- /dev/null +++ b/src/cli/util/gnunet-ecc.c | |||
@@ -0,0 +1,510 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012, 2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-ecc.c | ||
23 | * @brief tool to manipulate EDDSA key files | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_testing_lib.h" | ||
30 | #include <gcrypt.h> | ||
31 | |||
32 | /** | ||
33 | * Number of characters a Base32-encoded public key requires. | ||
34 | */ | ||
35 | #define KEY_STR_LEN sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) * 8 / 5 + 1 | ||
36 | |||
37 | /** | ||
38 | * Flag for listing public key. | ||
39 | */ | ||
40 | static int list_keys; | ||
41 | |||
42 | /** | ||
43 | * Flag for listing public key. | ||
44 | */ | ||
45 | static unsigned int list_keys_count; | ||
46 | |||
47 | /** | ||
48 | * Flag for printing public key. | ||
49 | */ | ||
50 | static int print_public_key; | ||
51 | |||
52 | /** | ||
53 | * Flag for printing private key. | ||
54 | */ | ||
55 | static int print_private_key; | ||
56 | |||
57 | /** | ||
58 | * Flag for printing public key in hex. | ||
59 | */ | ||
60 | static int print_public_key_hex; | ||
61 | |||
62 | /** | ||
63 | * Flag for printing the output of random example operations. | ||
64 | */ | ||
65 | static int print_examples_flag; | ||
66 | |||
67 | /** | ||
68 | * Option set to create a bunch of keys at once. | ||
69 | */ | ||
70 | static unsigned int make_keys; | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Create a flat file with a large number of key pairs for testing. | ||
75 | * | ||
76 | * @param fn File name to store the keys. | ||
77 | * @param prefix Desired prefix for the public keys, NULL if any key is OK. | ||
78 | */ | ||
79 | static void | ||
80 | create_keys (const char *fn, const char *prefix) | ||
81 | { | ||
82 | FILE *f; | ||
83 | struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
84 | struct GNUNET_CRYPTO_EddsaPublicKey target_pub; | ||
85 | static char vanity[KEY_STR_LEN + 1]; | ||
86 | size_t len; | ||
87 | size_t n; | ||
88 | size_t rest; | ||
89 | unsigned char mask; | ||
90 | unsigned target_byte; | ||
91 | char *s; | ||
92 | |||
93 | if (NULL == (f = fopen (fn, "w+"))) | ||
94 | { | ||
95 | fprintf (stderr, _ ("Failed to open `%s': %s\n"), fn, strerror (errno)); | ||
96 | return; | ||
97 | } | ||
98 | if (NULL != prefix) | ||
99 | { | ||
100 | len = GNUNET_strlcpy (vanity, prefix, sizeof(vanity)); | ||
101 | n = len * 5 / 8; | ||
102 | rest = len * 5 % 8; | ||
103 | |||
104 | memset (&vanity[len], '0', KEY_STR_LEN - len); | ||
105 | vanity[KEY_STR_LEN] = '\0'; | ||
106 | GNUNET_assert (GNUNET_OK == | ||
107 | GNUNET_CRYPTO_eddsa_public_key_from_string (vanity, | ||
108 | KEY_STR_LEN, | ||
109 | &target_pub)); | ||
110 | if (0 != rest) | ||
111 | { | ||
112 | /** | ||
113 | * Documentation by example: | ||
114 | * vanity = "A" | ||
115 | * len = 1 | ||
116 | * n = 5/8 = 0 (bytes) | ||
117 | * rest = 5%8 = 5 (bits) | ||
118 | * mask = ~(2**(8 - 5) - 1) = ~(2**3 - 1) = ~(8 - 1) = ~b111 = b11111000 | ||
119 | */mask = ~((int) pow (2, 8 - rest) - 1); | ||
120 | target_byte = ((unsigned char *) &target_pub)[n] & mask; | ||
121 | } | ||
122 | else | ||
123 | { | ||
124 | /* Just so old (debian) versions of GCC calm down with the warnings. */ | ||
125 | mask = target_byte = 0; | ||
126 | } | ||
127 | s = GNUNET_CRYPTO_eddsa_public_key_to_string (&target_pub); | ||
128 | fprintf (stderr, | ||
129 | _ ("Generating %u keys like %s, please wait"), | ||
130 | make_keys, | ||
131 | s); | ||
132 | GNUNET_free (s); | ||
133 | fprintf (stderr, "\nattempt %s [%u, %X]\n", vanity, (unsigned int) n, mask); | ||
134 | } | ||
135 | else | ||
136 | { | ||
137 | fprintf (stderr, _ ("Generating %u keys, please wait"), make_keys); | ||
138 | /* Just so old (debian) versions of GCC calm down with the warnings. */ | ||
139 | n = rest = target_byte = mask = 0; | ||
140 | } | ||
141 | |||
142 | while (0 < make_keys--) | ||
143 | { | ||
144 | fprintf (stderr, "."); | ||
145 | GNUNET_CRYPTO_eddsa_key_create (&pk); | ||
146 | if (NULL != prefix) | ||
147 | { | ||
148 | struct GNUNET_CRYPTO_EddsaPublicKey newkey; | ||
149 | |||
150 | GNUNET_CRYPTO_eddsa_key_get_public (&pk, | ||
151 | &newkey); | ||
152 | if (0 != memcmp (&target_pub, | ||
153 | &newkey, | ||
154 | n)) | ||
155 | { | ||
156 | make_keys++; | ||
157 | continue; | ||
158 | } | ||
159 | if (0 != rest) | ||
160 | { | ||
161 | unsigned char new_byte; | ||
162 | |||
163 | new_byte = ((unsigned char *) &newkey)[n] & mask; | ||
164 | if (target_byte != new_byte) | ||
165 | { | ||
166 | make_keys++; | ||
167 | continue; | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | if (GNUNET_TESTING_HOSTKEYFILESIZE != | ||
172 | fwrite (&pk, | ||
173 | 1, | ||
174 | GNUNET_TESTING_HOSTKEYFILESIZE, | ||
175 | f)) | ||
176 | { | ||
177 | fprintf (stderr, | ||
178 | _ ("\nFailed to write to `%s': %s\n"), | ||
179 | fn, | ||
180 | strerror (errno)); | ||
181 | break; | ||
182 | } | ||
183 | } | ||
184 | if (UINT_MAX == make_keys) | ||
185 | fprintf (stderr, _ ("\nFinished!\n")); | ||
186 | else | ||
187 | fprintf (stderr, _ ("\nError, %u keys not generated\n"), make_keys); | ||
188 | fclose (f); | ||
189 | } | ||
190 | |||
191 | |||
192 | static void | ||
193 | print_hex (const char *msg, const void *buf, size_t size) | ||
194 | { | ||
195 | printf ("%s: ", msg); | ||
196 | for (size_t i = 0; i < size; i++) | ||
197 | { | ||
198 | printf ("%02hhx", ((const uint8_t *) buf)[i]); | ||
199 | } | ||
200 | printf ("\n"); | ||
201 | } | ||
202 | |||
203 | |||
204 | static void | ||
205 | print_examples_ecdh (void) | ||
206 | { | ||
207 | struct GNUNET_CRYPTO_EcdhePrivateKey dh_priv1; | ||
208 | struct GNUNET_CRYPTO_EcdhePublicKey dh_pub1; | ||
209 | struct GNUNET_CRYPTO_EcdhePrivateKey dh_priv2; | ||
210 | struct GNUNET_CRYPTO_EcdhePublicKey dh_pub2; | ||
211 | struct GNUNET_HashCode hash; | ||
212 | char buf[128]; | ||
213 | |||
214 | GNUNET_CRYPTO_ecdhe_key_create (&dh_priv1); | ||
215 | GNUNET_CRYPTO_ecdhe_key_create (&dh_priv2); | ||
216 | GNUNET_CRYPTO_ecdhe_key_get_public (&dh_priv1, | ||
217 | &dh_pub1); | ||
218 | GNUNET_CRYPTO_ecdhe_key_get_public (&dh_priv2, | ||
219 | &dh_pub2); | ||
220 | |||
221 | GNUNET_assert (NULL != | ||
222 | GNUNET_STRINGS_data_to_string (&dh_priv1, | ||
223 | sizeof (dh_priv1), | ||
224 | buf, | ||
225 | sizeof (buf))); | ||
226 | printf ("ECDHE key 1:\n"); | ||
227 | printf ("private: %s\n", | ||
228 | buf); | ||
229 | print_hex ("private(hex)", | ||
230 | &dh_priv1, sizeof (dh_priv1)); | ||
231 | GNUNET_assert (NULL != | ||
232 | GNUNET_STRINGS_data_to_string (&dh_pub1, | ||
233 | sizeof (dh_pub1), | ||
234 | buf, | ||
235 | sizeof (buf))); | ||
236 | printf ("public: %s\n", | ||
237 | buf); | ||
238 | print_hex ("public(hex)", | ||
239 | &dh_pub1, | ||
240 | sizeof (dh_pub1)); | ||
241 | |||
242 | GNUNET_assert (NULL != | ||
243 | GNUNET_STRINGS_data_to_string (&dh_priv2, | ||
244 | sizeof (dh_priv2), | ||
245 | buf, | ||
246 | sizeof (buf))); | ||
247 | printf ("ECDHE key 2:\n"); | ||
248 | printf ("private: %s\n", buf); | ||
249 | print_hex ("private(hex)", | ||
250 | &dh_priv2, | ||
251 | sizeof (dh_priv2)); | ||
252 | GNUNET_assert (NULL != | ||
253 | GNUNET_STRINGS_data_to_string (&dh_pub2, | ||
254 | sizeof (dh_pub2), | ||
255 | buf, | ||
256 | sizeof (buf))); | ||
257 | printf ("public: %s\n", buf); | ||
258 | print_hex ("public(hex)", | ||
259 | &dh_pub2, | ||
260 | sizeof (dh_pub2)); | ||
261 | |||
262 | GNUNET_assert (GNUNET_OK == | ||
263 | GNUNET_CRYPTO_ecc_ecdh (&dh_priv1, | ||
264 | &dh_pub2, | ||
265 | &hash)); | ||
266 | GNUNET_assert (NULL != | ||
267 | GNUNET_STRINGS_data_to_string (&hash, | ||
268 | sizeof (hash), | ||
269 | buf, | ||
270 | sizeof (buf))); | ||
271 | printf ("ECDH shared secret: %s\n", | ||
272 | buf); | ||
273 | |||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Print some random example operations to stdout. | ||
279 | */ | ||
280 | static void | ||
281 | print_examples (void) | ||
282 | { | ||
283 | print_examples_ecdh (); | ||
284 | // print_examples_ecdsa (); | ||
285 | // print_examples_eddsa (); | ||
286 | } | ||
287 | |||
288 | |||
289 | static void | ||
290 | print_key (const char *filename) | ||
291 | { | ||
292 | struct GNUNET_DISK_FileHandle *fd; | ||
293 | struct GNUNET_CRYPTO_EddsaPrivateKey private_key; | ||
294 | struct GNUNET_CRYPTO_EddsaPublicKey public_key; | ||
295 | char *hostkeys_data; | ||
296 | char *hostkey_str; | ||
297 | uint64_t fs; | ||
298 | unsigned int total_hostkeys; | ||
299 | unsigned int c; | ||
300 | ssize_t sret; | ||
301 | |||
302 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
303 | { | ||
304 | fprintf (stderr, _ ("Hostkeys file `%s' not found\n"), filename); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | /* Check hostkey file size, read entire thing into memory */ | ||
309 | if (GNUNET_OK != | ||
310 | GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) | ||
311 | fs = 0; | ||
312 | if (0 == fs) | ||
313 | { | ||
314 | fprintf (stderr, _ ("Hostkeys file `%s' is empty\n"), filename); | ||
315 | return; /* File is empty */ | ||
316 | } | ||
317 | if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE)) | ||
318 | { | ||
319 | fprintf (stderr, _ ("Incorrect hostkey file format: %s\n"), filename); | ||
320 | return; | ||
321 | } | ||
322 | fd = GNUNET_DISK_file_open (filename, | ||
323 | GNUNET_DISK_OPEN_READ, | ||
324 | GNUNET_DISK_PERM_NONE); | ||
325 | if (NULL == fd) | ||
326 | { | ||
327 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename); | ||
328 | return; | ||
329 | } | ||
330 | hostkeys_data = GNUNET_malloc (fs); | ||
331 | sret = GNUNET_DISK_file_read (fd, hostkeys_data, fs); | ||
332 | if ((sret < 0) || (fs != (size_t) sret)) | ||
333 | { | ||
334 | fprintf (stderr, _ ("Could not read hostkey file: %s\n"), filename); | ||
335 | GNUNET_free (hostkeys_data); | ||
336 | GNUNET_DISK_file_close (fd); | ||
337 | return; | ||
338 | } | ||
339 | GNUNET_DISK_file_close (fd); | ||
340 | |||
341 | if (NULL == hostkeys_data) | ||
342 | return; | ||
343 | total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE; | ||
344 | for (c = 0; (c < total_hostkeys) && (c < list_keys_count); c++) | ||
345 | { | ||
346 | GNUNET_memcpy (&private_key, | ||
347 | hostkeys_data + (c * GNUNET_TESTING_HOSTKEYFILESIZE), | ||
348 | GNUNET_TESTING_HOSTKEYFILESIZE); | ||
349 | GNUNET_CRYPTO_eddsa_key_get_public (&private_key, &public_key); | ||
350 | hostkey_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&public_key); | ||
351 | if (NULL != hostkey_str) | ||
352 | { | ||
353 | fprintf (stderr, "%4u: %s\n", c, hostkey_str); | ||
354 | GNUNET_free (hostkey_str); | ||
355 | } | ||
356 | else | ||
357 | fprintf (stderr, "%4u: %s\n", c, "invalid"); | ||
358 | } | ||
359 | GNUNET_free (hostkeys_data); | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * Main function that will be run by the scheduler. | ||
365 | * | ||
366 | * @param cls closure, NULL | ||
367 | * @param args remaining command-line arguments | ||
368 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
369 | * @param cfg configuration | ||
370 | */ | ||
371 | static void | ||
372 | run (void *cls, | ||
373 | char *const *args, | ||
374 | const char *cfgfile, | ||
375 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
376 | { | ||
377 | (void) cls; | ||
378 | (void) cfgfile; | ||
379 | (void) cfg; | ||
380 | |||
381 | if (print_examples_flag) | ||
382 | { | ||
383 | print_examples (); | ||
384 | return; | ||
385 | } | ||
386 | if (NULL == args[0]) | ||
387 | { | ||
388 | fprintf (stderr, "%s", _ ("No hostkey file specified on command line\n")); | ||
389 | return; | ||
390 | } | ||
391 | if (list_keys) | ||
392 | { | ||
393 | print_key (args[0]); | ||
394 | return; | ||
395 | } | ||
396 | if (make_keys > 0) | ||
397 | { | ||
398 | create_keys (args[0], args[1]); | ||
399 | return; | ||
400 | } | ||
401 | if (print_public_key || print_public_key_hex || print_private_key) | ||
402 | { | ||
403 | char *str; | ||
404 | struct GNUNET_DISK_FileHandle *keyfile; | ||
405 | struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
406 | struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
407 | |||
408 | keyfile = GNUNET_DISK_file_open (args[0], | ||
409 | GNUNET_DISK_OPEN_READ, | ||
410 | GNUNET_DISK_PERM_NONE); | ||
411 | if (NULL == keyfile) | ||
412 | return; | ||
413 | while (sizeof(pk) == GNUNET_DISK_file_read (keyfile, &pk, sizeof(pk))) | ||
414 | { | ||
415 | GNUNET_CRYPTO_eddsa_key_get_public (&pk, &pub); | ||
416 | if (print_public_key_hex) | ||
417 | { | ||
418 | print_hex ("HEX:", &pub, sizeof(pub)); | ||
419 | } | ||
420 | else if (print_public_key) | ||
421 | { | ||
422 | str = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub); | ||
423 | fprintf (stdout, "%s\n", str); | ||
424 | GNUNET_free (str); | ||
425 | } | ||
426 | else if (print_private_key) | ||
427 | { | ||
428 | str = GNUNET_CRYPTO_eddsa_private_key_to_string (&pk); | ||
429 | fprintf (stdout, "%s\n", str); | ||
430 | GNUNET_free (str); | ||
431 | } | ||
432 | } | ||
433 | GNUNET_DISK_file_close (keyfile); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | |||
438 | /** | ||
439 | * Program to manipulate ECC key files. | ||
440 | * | ||
441 | * @param argc number of arguments from the command line | ||
442 | * @param argv command line arguments | ||
443 | * @return 0 ok, 1 on error | ||
444 | */ | ||
445 | int | ||
446 | main (int argc, char *const *argv) | ||
447 | { | ||
448 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
449 | { GNUNET_GETOPT_option_flag ('i', | ||
450 | "iterate", | ||
451 | gettext_noop ( | ||
452 | "list keys included in a file (for testing)"), | ||
453 | &list_keys), | ||
454 | GNUNET_GETOPT_option_uint ( | ||
455 | 'e', | ||
456 | "end=", | ||
457 | "COUNT", | ||
458 | gettext_noop ("number of keys to list included in a file (for testing)"), | ||
459 | &list_keys_count), | ||
460 | GNUNET_GETOPT_option_uint ( | ||
461 | 'g', | ||
462 | "generate-keys", | ||
463 | "COUNT", | ||
464 | gettext_noop ("create COUNT public-private key pairs (for testing)"), | ||
465 | &make_keys), | ||
466 | GNUNET_GETOPT_option_flag ('p', | ||
467 | "print-public-key", | ||
468 | gettext_noop ( | ||
469 | "print the public key in ASCII format"), | ||
470 | &print_public_key), | ||
471 | GNUNET_GETOPT_option_flag ('P', | ||
472 | "print-private-key", | ||
473 | gettext_noop ( | ||
474 | "print the private key in ASCII format"), | ||
475 | &print_private_key), | ||
476 | GNUNET_GETOPT_option_flag ('x', | ||
477 | "print-hex", | ||
478 | gettext_noop ( | ||
479 | "print the public key in HEX format"), | ||
480 | &print_public_key_hex), | ||
481 | GNUNET_GETOPT_option_flag ( | ||
482 | 'E', | ||
483 | "examples", | ||
484 | gettext_noop ( | ||
485 | "print examples of ECC operations (used for compatibility testing)"), | ||
486 | &print_examples_flag), | ||
487 | GNUNET_GETOPT_OPTION_END }; | ||
488 | int ret; | ||
489 | |||
490 | list_keys_count = UINT32_MAX; | ||
491 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
492 | return 2; | ||
493 | |||
494 | ret = (GNUNET_OK == | ||
495 | GNUNET_PROGRAM_run (argc, | ||
496 | argv, | ||
497 | "gnunet-ecc [OPTIONS] keyfile [VANITY_PREFIX]", | ||
498 | gettext_noop ( | ||
499 | "Manipulate GNUnet private ECC key files"), | ||
500 | options, | ||
501 | &run, | ||
502 | NULL)) | ||
503 | ? 0 | ||
504 | : 1; | ||
505 | GNUNET_free_nz ((void *) argv); | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | |||
510 | /* end of gnunet-ecc.c */ | ||
diff --git a/src/cli/util/gnunet-qr.c b/src/cli/util/gnunet-qr.c new file mode 100644 index 000000000..d9b873c05 --- /dev/null +++ b/src/cli/util/gnunet-qr.c | |||
@@ -0,0 +1,597 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013-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 util/gnunet-qr.c | ||
22 | * @author Hartmut Goebel (original implementation) | ||
23 | * @author Martin Schanzenbach (integrate gnunet-uri) | ||
24 | * @author Christian Grothoff (error handling) | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include <stdio.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <signal.h> | ||
30 | #include <zbar.h> | ||
31 | |||
32 | |||
33 | #include "gnunet_util_lib.h" | ||
34 | |||
35 | #if HAVE_PNG | ||
36 | #include <png.h> | ||
37 | #endif | ||
38 | |||
39 | /** | ||
40 | * Global exit code. | ||
41 | * Set to non-zero if an error occurs after the scheduler has started. | ||
42 | */ | ||
43 | static int exit_code = 0; | ||
44 | |||
45 | /** | ||
46 | * Video device to capture from. | ||
47 | * Used by default if PNG support is disabled or no PNG file is specified. | ||
48 | * Defaults to /dev/video0. | ||
49 | */ | ||
50 | static char *device = NULL; | ||
51 | |||
52 | #if HAVE_PNG | ||
53 | /** | ||
54 | * Name of the file to read from. | ||
55 | * If the file is not a PNG-encoded image of a QR code, an error will be | ||
56 | * thrown. | ||
57 | */ | ||
58 | static char *pngfilename = NULL; | ||
59 | #endif | ||
60 | |||
61 | /** | ||
62 | * Requested verbosity. | ||
63 | */ | ||
64 | static unsigned int verbosity = 0; | ||
65 | |||
66 | /** | ||
67 | * Child process handle. | ||
68 | */ | ||
69 | struct GNUNET_OS_Process *childproc = NULL; | ||
70 | |||
71 | /** | ||
72 | * Child process handle for waiting. | ||
73 | */ | ||
74 | static struct GNUNET_ChildWaitHandle *waitchildproc = NULL; | ||
75 | |||
76 | /** | ||
77 | * Macro to handle verbosity when printing messages. | ||
78 | */ | ||
79 | #define LOG(fmt, ...) \ | ||
80 | do \ | ||
81 | { \ | ||
82 | if (0 < verbosity) \ | ||
83 | { \ | ||
84 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, fmt, ##__VA_ARGS__); \ | ||
85 | if (verbosity > 1) \ | ||
86 | { \ | ||
87 | fprintf (stdout, fmt, ##__VA_ARGS__); \ | ||
88 | } \ | ||
89 | } \ | ||
90 | } \ | ||
91 | while (0) | ||
92 | |||
93 | /** | ||
94 | * Executed when program is terminating. | ||
95 | */ | ||
96 | static void | ||
97 | shutdown_program (void *cls) | ||
98 | { | ||
99 | if (NULL != waitchildproc) | ||
100 | { | ||
101 | GNUNET_wait_child_cancel (waitchildproc); | ||
102 | } | ||
103 | if (NULL != childproc) | ||
104 | { | ||
105 | /* A bit brutal, but this process is terminating so we're out of time */ | ||
106 | GNUNET_OS_process_kill (childproc, SIGKILL); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Callback executed when the child process terminates. | ||
112 | * | ||
113 | * @param cls closure | ||
114 | * @param type status of the child process | ||
115 | * @param code the exit code of the child process | ||
116 | */ | ||
117 | static void | ||
118 | wait_child (void *cls, | ||
119 | enum GNUNET_OS_ProcessStatusType type, | ||
120 | long unsigned int code) | ||
121 | { | ||
122 | GNUNET_OS_process_destroy (childproc); | ||
123 | childproc = NULL; | ||
124 | waitchildproc = NULL; | ||
125 | |||
126 | char *uri = cls; | ||
127 | |||
128 | if (0 != exit_code) | ||
129 | { | ||
130 | fprintf (stdout, _("Failed to add URI %s\n"), uri); | ||
131 | } | ||
132 | else | ||
133 | { | ||
134 | fprintf (stdout, _("Added URI %s\n"), uri); | ||
135 | } | ||
136 | |||
137 | GNUNET_free (uri); | ||
138 | |||
139 | GNUNET_SCHEDULER_shutdown (); | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * Dispatch URIs to the appropriate GNUnet helper process. | ||
144 | * | ||
145 | * @param cls closure | ||
146 | * @param uri URI to dispatch | ||
147 | * @param cfgfile name of the configuration file in use | ||
148 | * @param cfg the configuration in use | ||
149 | */ | ||
150 | static void | ||
151 | handle_uri (void *cls, | ||
152 | const char *uri, | ||
153 | const char *cfgfile, | ||
154 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
155 | { | ||
156 | const char *cursor = uri; | ||
157 | |||
158 | if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://"))) | ||
159 | { | ||
160 | fprintf (stderr, | ||
161 | _("Invalid URI: does not start with `gnunet://'\n")); | ||
162 | exit_code = 1; | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | cursor += strlen ("gnunet://"); | ||
167 | |||
168 | const char *slash = strchr (cursor, '/'); | ||
169 | if (NULL == slash) | ||
170 | { | ||
171 | fprintf (stderr, _("Invalid URI: fails to specify a subsystem\n")); | ||
172 | exit_code = 1; | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | char *subsystem = GNUNET_strndup (cursor, slash - cursor); | ||
177 | char *program = NULL; | ||
178 | |||
179 | if (GNUNET_OK != | ||
180 | GNUNET_CONFIGURATION_get_value_string (cfg, "uri", subsystem, &program)) | ||
181 | { | ||
182 | fprintf (stderr, _("No known handler for subsystem `%s'\n"), subsystem); | ||
183 | GNUNET_free (subsystem); | ||
184 | exit_code = 1; | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | GNUNET_free (subsystem); | ||
189 | |||
190 | char **childargv = NULL; | ||
191 | unsigned int childargc = 0; | ||
192 | |||
193 | for (const char *token=strtok (program, " "); | ||
194 | NULL!=token; | ||
195 | token=strtok(NULL, " ")) | ||
196 | { | ||
197 | GNUNET_array_append (childargv, childargc, GNUNET_strdup (token)); | ||
198 | } | ||
199 | GNUNET_array_append (childargv, childargc, GNUNET_strdup (uri)); | ||
200 | GNUNET_array_append (childargv, childargc, NULL); | ||
201 | |||
202 | childproc = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ALL, | ||
203 | NULL, | ||
204 | NULL, | ||
205 | NULL, | ||
206 | childargv[0], | ||
207 | childargv); | ||
208 | for (size_t i=0; i<childargc-1; ++i) | ||
209 | { | ||
210 | GNUNET_free (childargv[i]); | ||
211 | } | ||
212 | |||
213 | GNUNET_array_grow (childargv, childargc, 0); | ||
214 | |||
215 | if (NULL == childproc) | ||
216 | { | ||
217 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
218 | _("Unable to start child process `%s'\n"), | ||
219 | program); | ||
220 | GNUNET_free (program); | ||
221 | exit_code = 1; | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | waitchildproc = GNUNET_wait_child (childproc, &wait_child, (void *)uri); | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * Obtain a QR code symbol from @a proc. | ||
230 | * | ||
231 | * @param proc the zbar processor to use | ||
232 | * @return NULL on error | ||
233 | */ | ||
234 | static const zbar_symbol_t * | ||
235 | get_symbol (zbar_processor_t *proc) | ||
236 | { | ||
237 | if (0 != zbar_processor_parse_config (proc, "enable")) | ||
238 | { | ||
239 | GNUNET_break (0); | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | int r = zbar_processor_init (proc, device, 1); | ||
244 | if (0 != r) | ||
245 | { | ||
246 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
247 | _("Failed to open device: `%s': %d\n"), | ||
248 | device, | ||
249 | r); | ||
250 | return NULL; | ||
251 | } | ||
252 | |||
253 | r = zbar_processor_set_visible (proc, 1); | ||
254 | r += zbar_processor_set_active (proc, 1); | ||
255 | if (0 != r) | ||
256 | { | ||
257 | GNUNET_break (0); | ||
258 | return NULL; | ||
259 | } | ||
260 | |||
261 | LOG (_("Capturing...\n")); | ||
262 | |||
263 | int n = zbar_process_one (proc, -1); | ||
264 | |||
265 | zbar_processor_set_active (proc, 0); | ||
266 | zbar_processor_set_visible (proc, 0); | ||
267 | |||
268 | if (-1 == n) | ||
269 | { | ||
270 | LOG (_("No captured images\n")); | ||
271 | return NULL; | ||
272 | } | ||
273 | |||
274 | LOG(_("Got %d images\n"), n); | ||
275 | |||
276 | const zbar_symbol_set_t *symbols = zbar_processor_get_results (proc); | ||
277 | if (NULL == symbols) | ||
278 | { | ||
279 | GNUNET_break (0); | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | return zbar_symbol_set_first_symbol (symbols); | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * Run the zbar QR code parser. | ||
288 | * | ||
289 | * @return NULL on error | ||
290 | */ | ||
291 | static char * | ||
292 | run_zbar (void) | ||
293 | { | ||
294 | zbar_processor_t *proc = zbar_processor_create (1); | ||
295 | if (NULL == proc) | ||
296 | { | ||
297 | GNUNET_break (0); | ||
298 | return NULL; | ||
299 | } | ||
300 | |||
301 | if (NULL == device) | ||
302 | { | ||
303 | device = GNUNET_strdup ("/dev/video0"); | ||
304 | } | ||
305 | |||
306 | const zbar_symbol_t *symbol = get_symbol (proc); | ||
307 | if (NULL == symbol) | ||
308 | { | ||
309 | zbar_processor_destroy (proc); | ||
310 | return NULL; | ||
311 | } | ||
312 | |||
313 | const char *data = zbar_symbol_get_data (symbol); | ||
314 | if (NULL == data) | ||
315 | { | ||
316 | GNUNET_break (0); | ||
317 | zbar_processor_destroy (proc); | ||
318 | return NULL; | ||
319 | } | ||
320 | |||
321 | LOG (_("Found %s: \"%s\"\n"), | ||
322 | zbar_get_symbol_name (zbar_symbol_get_type (symbol)), | ||
323 | data); | ||
324 | |||
325 | char *copy = GNUNET_strdup (data); | ||
326 | |||
327 | zbar_processor_destroy (proc); | ||
328 | GNUNET_free (device); | ||
329 | |||
330 | return copy; | ||
331 | } | ||
332 | |||
333 | #if HAVE_PNG | ||
334 | /** | ||
335 | * Decode the PNG-encoded file to a raw byte buffer. | ||
336 | * | ||
337 | * @param width[out] where to store the image width | ||
338 | * @param height[out] where to store the image height | ||
339 | */ | ||
340 | static char * | ||
341 | png_parse (uint32_t *width, uint32_t *height) | ||
342 | { | ||
343 | if (NULL == width || NULL == height) | ||
344 | { | ||
345 | return NULL; | ||
346 | } | ||
347 | |||
348 | FILE *pngfile = fopen (pngfilename, "rb"); | ||
349 | if (NULL == pngfile) | ||
350 | { | ||
351 | return NULL; | ||
352 | } | ||
353 | |||
354 | unsigned char header[8]; | ||
355 | if (8 != fread (header, 1, 8, pngfile)) | ||
356 | { | ||
357 | fclose (pngfile); | ||
358 | return NULL; | ||
359 | } | ||
360 | |||
361 | if (png_sig_cmp (header, 0, 8)) | ||
362 | { | ||
363 | fclose (pngfile); | ||
364 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
365 | _("%s is not a PNG file\n"), | ||
366 | pngfilename); | ||
367 | fprintf (stderr, _("%s is not a PNG file\n"), pngfilename); | ||
368 | return NULL; | ||
369 | } | ||
370 | |||
371 | /* libpng's default error handling might or might not conflict with GNUnet's | ||
372 | scheduler and event loop. Beware of strange interactions. */ | ||
373 | png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, | ||
374 | NULL, | ||
375 | NULL, | ||
376 | NULL); | ||
377 | if (NULL == png) | ||
378 | { | ||
379 | GNUNET_break (0); | ||
380 | fclose (pngfile); | ||
381 | return NULL; | ||
382 | } | ||
383 | |||
384 | png_infop pnginfo = png_create_info_struct (png); | ||
385 | if (NULL == pnginfo) | ||
386 | { | ||
387 | GNUNET_break (0); | ||
388 | png_destroy_read_struct (&png, NULL, NULL); | ||
389 | fclose (pngfile); | ||
390 | return NULL; | ||
391 | } | ||
392 | |||
393 | if (setjmp (png_jmpbuf (png))) | ||
394 | { | ||
395 | GNUNET_break (0); | ||
396 | png_destroy_read_struct (&png, &pnginfo, NULL); | ||
397 | fclose (pngfile); | ||
398 | return NULL; | ||
399 | } | ||
400 | |||
401 | png_init_io (png, pngfile); | ||
402 | png_set_sig_bytes (png, 8); | ||
403 | |||
404 | png_read_info (png, pnginfo); | ||
405 | |||
406 | png_byte pngcolor = png_get_color_type (png, pnginfo); | ||
407 | png_byte pngdepth = png_get_bit_depth (png, pnginfo); | ||
408 | |||
409 | /* Normalize picture --- based on a zbar example */ | ||
410 | if (0 != (pngcolor & PNG_COLOR_TYPE_PALETTE)) | ||
411 | { | ||
412 | png_set_palette_to_rgb (png); | ||
413 | } | ||
414 | |||
415 | if (pngcolor == PNG_COLOR_TYPE_GRAY && pngdepth < 8) | ||
416 | { | ||
417 | png_set_expand_gray_1_2_4_to_8 (png); | ||
418 | } | ||
419 | |||
420 | if (16 == pngdepth) | ||
421 | { | ||
422 | png_set_strip_16 (png); | ||
423 | } | ||
424 | |||
425 | if (0 != (pngcolor & PNG_COLOR_MASK_ALPHA)) | ||
426 | { | ||
427 | png_set_strip_alpha (png); | ||
428 | } | ||
429 | |||
430 | if (0 != (pngcolor & PNG_COLOR_MASK_COLOR)) | ||
431 | { | ||
432 | png_set_rgb_to_gray_fixed (png, 1, -1, -1); | ||
433 | } | ||
434 | |||
435 | png_uint_32 pngwidth = png_get_image_width (png, pnginfo); | ||
436 | png_uint_32 pngheight = png_get_image_height (png, pnginfo); | ||
437 | |||
438 | char *buffer = GNUNET_new_array (pngwidth * pngheight, char); | ||
439 | png_bytepp rows = GNUNET_new_array (pngheight, png_bytep); | ||
440 | |||
441 | for (png_uint_32 i=0; i<pngheight; ++i) | ||
442 | { | ||
443 | rows[i] = (unsigned char *)buffer + (pngwidth * i); | ||
444 | } | ||
445 | |||
446 | png_read_image (png, rows); | ||
447 | |||
448 | GNUNET_free (rows); | ||
449 | fclose (pngfile); | ||
450 | |||
451 | *width = pngwidth; | ||
452 | *height = pngheight; | ||
453 | |||
454 | return buffer; | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * Parse a PNG-encoded file for a QR code. | ||
459 | * | ||
460 | * @return NULL on error | ||
461 | */ | ||
462 | static char * | ||
463 | run_png_reader (void) | ||
464 | { | ||
465 | uint32_t width = 0; | ||
466 | uint32_t height = 0; | ||
467 | char *buffer = png_parse (&width, &height); | ||
468 | if (NULL == buffer) | ||
469 | { | ||
470 | return NULL; | ||
471 | } | ||
472 | |||
473 | zbar_image_scanner_t *scanner = zbar_image_scanner_create (); | ||
474 | zbar_image_scanner_set_config (scanner,0, ZBAR_CFG_ENABLE, 1); | ||
475 | |||
476 | zbar_image_t *zimage = zbar_image_create (); | ||
477 | zbar_image_set_format (zimage, zbar_fourcc ('Y', '8', '0', '0')); | ||
478 | zbar_image_set_size (zimage, width, height); | ||
479 | zbar_image_set_data (zimage, buffer, width * height, &zbar_image_free_data); | ||
480 | |||
481 | int n = zbar_scan_image (scanner, zimage); | ||
482 | |||
483 | if (-1 == n) | ||
484 | { | ||
485 | LOG (_("No captured images\n")); | ||
486 | return NULL; | ||
487 | } | ||
488 | |||
489 | LOG(_("Got %d images\n"), n); | ||
490 | |||
491 | const zbar_symbol_t *symbol = zbar_image_first_symbol (zimage); | ||
492 | |||
493 | const char *data = zbar_symbol_get_data (symbol); | ||
494 | if (NULL == data) | ||
495 | { | ||
496 | GNUNET_break (0); | ||
497 | zbar_image_destroy (zimage); | ||
498 | zbar_image_scanner_destroy (scanner); | ||
499 | return NULL; | ||
500 | } | ||
501 | |||
502 | LOG (_("Found %s: \"%s\"\n"), | ||
503 | zbar_get_symbol_name (zbar_symbol_get_type (symbol)), | ||
504 | data); | ||
505 | |||
506 | char *copy = GNUNET_strdup (data); | ||
507 | |||
508 | zbar_image_destroy (zimage); | ||
509 | zbar_image_scanner_destroy (scanner); | ||
510 | |||
511 | return copy; | ||
512 | } | ||
513 | #endif | ||
514 | |||
515 | /** | ||
516 | * Main function executed by the scheduler. | ||
517 | * | ||
518 | * @param cls closure | ||
519 | * @param args remaining command line arguments | ||
520 | * @param cfgfile name of the configuration file being used | ||
521 | * @param cfg the used configuration | ||
522 | */ | ||
523 | static void | ||
524 | run (void *cls, | ||
525 | char *const *args, | ||
526 | const char *cfgfile, | ||
527 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
528 | { | ||
529 | char *data = NULL; | ||
530 | |||
531 | GNUNET_SCHEDULER_add_shutdown (&shutdown_program, NULL); | ||
532 | |||
533 | #if HAVE_PNG | ||
534 | if (NULL != pngfilename) | ||
535 | { | ||
536 | data = run_png_reader (); | ||
537 | } | ||
538 | else | ||
539 | #endif | ||
540 | { | ||
541 | data = run_zbar (); | ||
542 | } | ||
543 | |||
544 | if (NULL == data) | ||
545 | { | ||
546 | LOG (_("No data found\n")); | ||
547 | exit_code = 1; | ||
548 | GNUNET_SCHEDULER_shutdown (); | ||
549 | return; | ||
550 | } | ||
551 | |||
552 | handle_uri (cls, data, cfgfile, cfg); | ||
553 | |||
554 | if (0 != exit_code) | ||
555 | { | ||
556 | fprintf (stdout, _("Failed to add URI %s\n"), data); | ||
557 | GNUNET_free (data); | ||
558 | GNUNET_SCHEDULER_shutdown (); | ||
559 | return; | ||
560 | } | ||
561 | |||
562 | LOG (_("Dispatching the URI\n")); | ||
563 | } | ||
564 | |||
565 | int | ||
566 | main (int argc, char *const *argv) | ||
567 | { | ||
568 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
569 | GNUNET_GETOPT_option_string ( | ||
570 | 'd', | ||
571 | "device", | ||
572 | "DEVICE", | ||
573 | gettext_noop ("use the video device DEVICE (defaults to /dev/video0)"), | ||
574 | &device), | ||
575 | #if HAVE_PNG | ||
576 | GNUNET_GETOPT_option_string ( | ||
577 | 'f', | ||
578 | "file", | ||
579 | "FILE", | ||
580 | gettext_noop ("read from the PNG-encoded file FILE"), | ||
581 | &pngfilename), | ||
582 | #endif | ||
583 | GNUNET_GETOPT_option_verbose (&verbosity), | ||
584 | GNUNET_GETOPT_OPTION_END, | ||
585 | }; | ||
586 | |||
587 | enum GNUNET_GenericReturnValue ret = | ||
588 | GNUNET_PROGRAM_run (argc, | ||
589 | argv, | ||
590 | "gnunet-qr", | ||
591 | gettext_noop ("Scan a QR code and import the URI read"), | ||
592 | options, | ||
593 | &run, | ||
594 | NULL); | ||
595 | |||
596 | return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1; | ||
597 | } | ||
diff --git a/src/cli/util/gnunet-resolver.c b/src/cli/util/gnunet-resolver.c new file mode 100644 index 000000000..a23aeb4aa --- /dev/null +++ b/src/cli/util/gnunet-resolver.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-resolver.c | ||
23 | * @brief tool to test resolver | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_resolver_service.h" | ||
30 | |||
31 | #define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
32 | |||
33 | /** | ||
34 | * Flag for reverse lookup. | ||
35 | */ | ||
36 | static int reverse; | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Prints each hostname obtained from DNS. | ||
41 | * | ||
42 | * @param cls closure (unused) | ||
43 | * @param hostname one of the names for the host, NULL | ||
44 | * on the last call to the callback | ||
45 | */ | ||
46 | static void | ||
47 | print_hostname (void *cls, | ||
48 | const char *hostname) | ||
49 | { | ||
50 | (void) cls; | ||
51 | if (NULL == hostname) | ||
52 | return; | ||
53 | fprintf (stdout, | ||
54 | "%s\n", | ||
55 | hostname); | ||
56 | } | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Callback function to display address. | ||
61 | * | ||
62 | * @param cls closure (unused) | ||
63 | * @param addr one of the addresses of the host, NULL for the last address | ||
64 | * @param addrlen length of the address | ||
65 | */ | ||
66 | static void | ||
67 | print_sockaddr (void *cls, | ||
68 | const struct sockaddr *addr, | ||
69 | socklen_t addrlen) | ||
70 | { | ||
71 | (void) cls; | ||
72 | if (NULL == addr) | ||
73 | return; | ||
74 | fprintf (stdout, | ||
75 | "%s\n", | ||
76 | GNUNET_a2s (addr, | ||
77 | addrlen)); | ||
78 | } | ||
79 | |||
80 | |||
81 | /** | ||
82 | * Main function that will be run by the scheduler. | ||
83 | * | ||
84 | * @param cls closure | ||
85 | * @param args remaining command-line arguments | ||
86 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
87 | * @param cfg configuration | ||
88 | */ | ||
89 | static void | ||
90 | run (void *cls, | ||
91 | char *const *args, | ||
92 | const char *cfgfile, | ||
93 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
94 | { | ||
95 | const struct sockaddr *sa; | ||
96 | socklen_t salen; | ||
97 | struct sockaddr_in v4; | ||
98 | struct sockaddr_in6 v6; | ||
99 | |||
100 | (void) cls; | ||
101 | (void) cfgfile; | ||
102 | (void) cfg; | ||
103 | if (NULL == args[0]) | ||
104 | return; | ||
105 | if (! reverse) | ||
106 | { | ||
107 | GNUNET_RESOLVER_ip_get (args[0], | ||
108 | AF_UNSPEC, | ||
109 | GET_TIMEOUT, | ||
110 | &print_sockaddr, | ||
111 | NULL); | ||
112 | return; | ||
113 | } | ||
114 | |||
115 | sa = NULL; | ||
116 | memset (&v4, 0, sizeof(v4)); | ||
117 | v4.sin_family = AF_INET; | ||
118 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
119 | v4.sin_len = sizeof(v4); | ||
120 | #endif | ||
121 | if (1 == inet_pton (AF_INET, | ||
122 | args[0], | ||
123 | &v4.sin_addr)) | ||
124 | { | ||
125 | sa = (struct sockaddr *) &v4; | ||
126 | salen = sizeof(v4); | ||
127 | } | ||
128 | memset (&v6, 0, sizeof(v6)); | ||
129 | v6.sin6_family = AF_INET6; | ||
130 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
131 | v6.sin6_len = sizeof(v6); | ||
132 | #endif | ||
133 | if (1 == inet_pton (AF_INET6, | ||
134 | args[0], | ||
135 | &v6.sin6_addr)) | ||
136 | { | ||
137 | sa = (struct sockaddr *) &v6; | ||
138 | salen = sizeof(v6); | ||
139 | } | ||
140 | if (NULL == sa) | ||
141 | { | ||
142 | fprintf (stderr, | ||
143 | "`%s' is not a valid IP: %s\n", | ||
144 | args[0], | ||
145 | strerror (errno)); | ||
146 | return; | ||
147 | } | ||
148 | GNUNET_RESOLVER_hostname_get (sa, salen, | ||
149 | GNUNET_YES, | ||
150 | GET_TIMEOUT, | ||
151 | &print_hostname, | ||
152 | NULL); | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * The main function to access GNUnet's DNS resolver. | ||
158 | * | ||
159 | * @param argc number of arguments from the command line | ||
160 | * @param argv command line arguments | ||
161 | * @return 0 ok, 1 on error | ||
162 | */ | ||
163 | int | ||
164 | main (int argc, char *const *argv) | ||
165 | { | ||
166 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
167 | GNUNET_GETOPT_option_flag ('r', | ||
168 | "reverse", | ||
169 | gettext_noop ("perform a reverse lookup"), | ||
170 | &reverse), | ||
171 | GNUNET_GETOPT_OPTION_END | ||
172 | }; | ||
173 | int ret; | ||
174 | |||
175 | if (GNUNET_OK != | ||
176 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
177 | &argc, &argv)) | ||
178 | return 2; | ||
179 | |||
180 | ret = (GNUNET_OK == | ||
181 | GNUNET_PROGRAM_run (argc, argv, | ||
182 | "gnunet-resolver [hostname]", | ||
183 | gettext_noop ("Use built-in GNUnet stub resolver"), | ||
184 | options, | ||
185 | &run, NULL)) ? 0 : 1; | ||
186 | GNUNET_free_nz ((void *) argv); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | |||
191 | /* end of gnunet-resolver.c */ | ||
diff --git a/src/cli/util/gnunet-scrypt.c b/src/cli/util/gnunet-scrypt.c new file mode 100644 index 000000000..3d1b9c017 --- /dev/null +++ b/src/cli/util/gnunet-scrypt.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2014 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 util/gnunet-scrypt.c | ||
22 | * @brief tool to manipulate SCRYPT proofs of work. | ||
23 | * @author Bart Polot | ||
24 | */ | ||
25 | |||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include <gcrypt.h> | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Salt for PoW calcualations. | ||
33 | */ | ||
34 | static struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. | ||
39 | */ | ||
40 | static unsigned long long nse_work_required; | ||
41 | |||
42 | /** | ||
43 | * Interval between proof find runs. | ||
44 | */ | ||
45 | static struct GNUNET_TIME_Relative proof_find_delay; | ||
46 | |||
47 | static struct GNUNET_CRYPTO_EddsaPublicKey pub; | ||
48 | |||
49 | static uint64_t proof; | ||
50 | |||
51 | static struct GNUNET_SCHEDULER_Task *proof_task; | ||
52 | |||
53 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
54 | |||
55 | static char *pkfn; | ||
56 | |||
57 | static char *pwfn; | ||
58 | |||
59 | |||
60 | /** | ||
61 | * Write our current proof to disk. | ||
62 | * | ||
63 | * @param cls closure | ||
64 | */ | ||
65 | static void | ||
66 | shutdown_task (void *cls) | ||
67 | { | ||
68 | (void) cls; | ||
69 | if (GNUNET_OK != | ||
70 | GNUNET_DISK_fn_write (pwfn, | ||
71 | &proof, | ||
72 | sizeof(proof), | ||
73 | GNUNET_DISK_PERM_USER_READ | ||
74 | | GNUNET_DISK_PERM_USER_WRITE)) | ||
75 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
76 | "write", | ||
77 | pwfn); | ||
78 | } | ||
79 | |||
80 | |||
81 | /** | ||
82 | * Find our proof of work. | ||
83 | * | ||
84 | * @param cls closure (unused) | ||
85 | */ | ||
86 | static void | ||
87 | find_proof (void *cls) | ||
88 | { | ||
89 | #define ROUND_SIZE 10 | ||
90 | uint64_t counter; | ||
91 | char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) | ||
92 | + sizeof(uint64_t)] GNUNET_ALIGN; | ||
93 | struct GNUNET_HashCode result; | ||
94 | unsigned int i; | ||
95 | struct GNUNET_TIME_Absolute timestamp; | ||
96 | struct GNUNET_TIME_Relative elapsed; | ||
97 | |||
98 | (void) cls; | ||
99 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
100 | "Got Proof of Work %llu\n", | ||
101 | (unsigned long long) proof); | ||
102 | proof_task = NULL; | ||
103 | GNUNET_memcpy (&buf[sizeof(uint64_t)], | ||
104 | &pub, | ||
105 | sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)); | ||
106 | i = 0; | ||
107 | counter = proof; | ||
108 | timestamp = GNUNET_TIME_absolute_get (); | ||
109 | while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) | ||
110 | { | ||
111 | GNUNET_memcpy (buf, &counter, sizeof(uint64_t)); | ||
112 | GNUNET_CRYPTO_pow_hash (&salt, | ||
113 | buf, | ||
114 | sizeof(buf), | ||
115 | &result); | ||
116 | if (nse_work_required <= | ||
117 | GNUNET_CRYPTO_hash_count_leading_zeros (&result)) | ||
118 | { | ||
119 | proof = counter; | ||
120 | fprintf (stdout, | ||
121 | "Proof of work found: %llu!\n", | ||
122 | (unsigned long long) proof); | ||
123 | GNUNET_SCHEDULER_shutdown (); | ||
124 | return; | ||
125 | } | ||
126 | counter++; | ||
127 | i++; | ||
128 | } | ||
129 | elapsed = GNUNET_TIME_absolute_get_duration (timestamp); | ||
130 | elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE); | ||
131 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
132 | "Current: %llu [%s/proof]\n", | ||
133 | (unsigned long long) counter, | ||
134 | GNUNET_STRINGS_relative_time_to_string (elapsed, 0)); | ||
135 | if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE)) | ||
136 | { | ||
137 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
138 | "Testing proofs currently at %llu\n", | ||
139 | (unsigned long long) counter); | ||
140 | /* remember progress every 100 rounds */ | ||
141 | proof = counter; | ||
142 | shutdown_task (NULL); | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | proof = counter; | ||
147 | } | ||
148 | proof_task = | ||
149 | GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay, | ||
150 | GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
151 | &find_proof, | ||
152 | NULL); | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Main function that will be run by the scheduler. | ||
158 | * | ||
159 | * @param cls closure | ||
160 | * @param args remaining command-line arguments | ||
161 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
162 | * @param cfg configuration | ||
163 | */ | ||
164 | static void | ||
165 | run (void *cls, | ||
166 | char *const *args, | ||
167 | const char *cfgfile, | ||
168 | const struct GNUNET_CONFIGURATION_Handle *config) | ||
169 | { | ||
170 | struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
171 | char *pids; | ||
172 | |||
173 | (void) cls; | ||
174 | (void) args; | ||
175 | (void) cfgfile; | ||
176 | cfg = config; | ||
177 | /* load proof of work */ | ||
178 | if (NULL == pwfn) | ||
179 | { | ||
180 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
181 | "NSE", | ||
182 | "PROOFFILE", | ||
183 | &pwfn)) | ||
184 | { | ||
185 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE"); | ||
186 | GNUNET_SCHEDULER_shutdown (); | ||
187 | return; | ||
188 | } | ||
189 | } | ||
190 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Proof of Work file: %s\n", pwfn); | ||
191 | if ((GNUNET_YES != GNUNET_DISK_file_test (pwfn)) || | ||
192 | (sizeof(proof) != GNUNET_DISK_fn_read (pwfn, &proof, sizeof(proof)))) | ||
193 | proof = 0; | ||
194 | |||
195 | /* load private key */ | ||
196 | if (NULL == pkfn) | ||
197 | { | ||
198 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
199 | "PEER", | ||
200 | "PRIVATE_KEY", | ||
201 | &pkfn)) | ||
202 | { | ||
203 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
204 | "PEER", | ||
205 | "PRIVATE_KEY"); | ||
206 | return; | ||
207 | } | ||
208 | } | ||
209 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn); | ||
210 | if (GNUNET_SYSERR == | ||
211 | GNUNET_CRYPTO_eddsa_key_from_file (pkfn, | ||
212 | GNUNET_YES, | ||
213 | &pk)) | ||
214 | { | ||
215 | fprintf (stderr, _ ("Loading hostkey from `%s' failed.\n"), pkfn); | ||
216 | GNUNET_free (pkfn); | ||
217 | return; | ||
218 | } | ||
219 | GNUNET_free (pkfn); | ||
220 | GNUNET_CRYPTO_eddsa_key_get_public (&pk, | ||
221 | &pub); | ||
222 | pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub); | ||
223 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer ID: %s\n", pids); | ||
224 | GNUNET_free (pids); | ||
225 | |||
226 | /* get target bit amount */ | ||
227 | if (0 == nse_work_required) | ||
228 | { | ||
229 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, | ||
230 | "NSE", | ||
231 | "WORKBITS", | ||
232 | &nse_work_required)) | ||
233 | { | ||
234 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS"); | ||
235 | GNUNET_SCHEDULER_shutdown (); | ||
236 | return; | ||
237 | } | ||
238 | if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8) | ||
239 | { | ||
240 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
241 | "NSE", | ||
242 | "WORKBITS", | ||
243 | _ ("Value is too large.\n")); | ||
244 | GNUNET_SCHEDULER_shutdown (); | ||
245 | return; | ||
246 | } | ||
247 | else if (0 == nse_work_required) | ||
248 | { | ||
249 | GNUNET_SCHEDULER_shutdown (); | ||
250 | return; | ||
251 | } | ||
252 | } | ||
253 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bits: %llu\n", nse_work_required); | ||
254 | |||
255 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
256 | "Delay between tries: %s\n", | ||
257 | GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1)); | ||
258 | proof_task = | ||
259 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
260 | &find_proof, | ||
261 | NULL); | ||
262 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
263 | } | ||
264 | |||
265 | |||
266 | /** | ||
267 | * Program to manipulate ECC key files. | ||
268 | * | ||
269 | * @param argc number of arguments from the command line | ||
270 | * @param argv command line arguments | ||
271 | * @return 0 ok, 1 on error | ||
272 | */ | ||
273 | int | ||
274 | main (int argc, char *const *argv) | ||
275 | { | ||
276 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
277 | GNUNET_GETOPT_option_ulong ( | ||
278 | 'b', | ||
279 | "bits", | ||
280 | "BITS", | ||
281 | gettext_noop ("number of bits to require for the proof of work"), | ||
282 | &nse_work_required), | ||
283 | GNUNET_GETOPT_option_filename ( | ||
284 | 'k', | ||
285 | "keyfile", | ||
286 | "FILE", | ||
287 | gettext_noop ("file with private key, otherwise default is used"), | ||
288 | &pkfn), | ||
289 | GNUNET_GETOPT_option_filename ( | ||
290 | 'o', | ||
291 | "outfile", | ||
292 | "FILE", | ||
293 | gettext_noop ("file with proof of work, otherwise default is used"), | ||
294 | &pwfn), | ||
295 | GNUNET_GETOPT_option_relative_time ('t', | ||
296 | "timeout", | ||
297 | "TIME", | ||
298 | gettext_noop ( | ||
299 | "time to wait between calculations"), | ||
300 | &proof_find_delay), | ||
301 | GNUNET_GETOPT_OPTION_END | ||
302 | }; | ||
303 | int ret; | ||
304 | |||
305 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
306 | return 2; | ||
307 | |||
308 | ret = | ||
309 | (GNUNET_OK == | ||
310 | GNUNET_PROGRAM_run (argc, | ||
311 | argv, | ||
312 | "gnunet-scrypt [OPTIONS] prooffile", | ||
313 | gettext_noop ("Manipulate GNUnet proof of work files"), | ||
314 | options, | ||
315 | &run, | ||
316 | NULL)) | ||
317 | ? 0 | ||
318 | : 1; | ||
319 | GNUNET_free_nz ((void *) argv); | ||
320 | GNUNET_free (pwfn); | ||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | |||
325 | /* end of gnunet-scrypt.c */ | ||
diff --git a/src/cli/util/gnunet-timeout.c b/src/cli/util/gnunet-timeout.c new file mode 100644 index 000000000..1d3002c08 --- /dev/null +++ b/src/cli/util/gnunet-timeout.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2010 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, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/util/gnunet-timeout.c | ||
23 | * @brief small tool starting a child process, waiting that it terminates or killing it after a given timeout period | ||
24 | * @author Matthias Wachs | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/wait.h> | ||
30 | #include <signal.h> | ||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <unistd.h> | ||
34 | |||
35 | static pid_t child; | ||
36 | |||
37 | |||
38 | static void | ||
39 | sigchld_handler (int val) | ||
40 | { | ||
41 | int status = 0; | ||
42 | int ret = 0; | ||
43 | |||
44 | (void) val; | ||
45 | waitpid (child, &status, 0); | ||
46 | if (WIFEXITED (status) != 0) | ||
47 | { | ||
48 | ret = WEXITSTATUS (status); | ||
49 | _exit (ret); /* return same status code */ | ||
50 | } | ||
51 | if (WIFSIGNALED (status) != 0) | ||
52 | { | ||
53 | ret = WTERMSIG (status); | ||
54 | kill (getpid (), ret); /* kill self with the same signal */ | ||
55 | } | ||
56 | _exit (-1); | ||
57 | } | ||
58 | |||
59 | |||
60 | static void | ||
61 | sigint_handler (int val) | ||
62 | { | ||
63 | kill (0, val); | ||
64 | _exit (val); | ||
65 | } | ||
66 | |||
67 | |||
68 | int | ||
69 | main (int argc, char *argv[]) | ||
70 | { | ||
71 | int timeout = 0; | ||
72 | pid_t gpid = 0; | ||
73 | |||
74 | if (argc < 3) | ||
75 | { | ||
76 | fprintf (stderr, | ||
77 | "arg 1: timeout in sec., arg 2: executable, arg<n> arguments\n"); | ||
78 | exit (-1); | ||
79 | } | ||
80 | |||
81 | timeout = atoi (argv[1]); | ||
82 | |||
83 | if (timeout == 0) | ||
84 | timeout = 600; | ||
85 | |||
86 | /* with getpgid() it does not compile, but getpgrp is the BSD version and working */ | ||
87 | gpid = getpgrp (); | ||
88 | |||
89 | signal (SIGCHLD, sigchld_handler); | ||
90 | signal (SIGABRT, sigint_handler); | ||
91 | signal (SIGFPE, sigint_handler); | ||
92 | signal (SIGILL, sigint_handler); | ||
93 | signal (SIGINT, sigint_handler); | ||
94 | signal (SIGSEGV, sigint_handler); | ||
95 | signal (SIGTERM, sigint_handler); | ||
96 | |||
97 | child = fork (); | ||
98 | if (child == 0) | ||
99 | { | ||
100 | /* int setpgrp(pid_t pid, pid_t pgid); is not working on this machine */ | ||
101 | // setpgrp (0, pid_t gpid); | ||
102 | if (-1 != gpid) | ||
103 | setpgid (0, gpid); | ||
104 | execvp (argv[2], &argv[2]); | ||
105 | exit (-1); | ||
106 | } | ||
107 | if (child > 0) | ||
108 | { | ||
109 | sleep (timeout); | ||
110 | printf ("Child processes were killed after timeout of %u seconds\n", | ||
111 | timeout); | ||
112 | kill (0, SIGTERM); | ||
113 | exit (3); | ||
114 | } | ||
115 | exit (-1); | ||
116 | } | ||
117 | |||
118 | |||
119 | /* end of timeout_watchdog.c */ | ||
diff --git a/src/cli/util/gnunet-uri.c b/src/cli/util/gnunet-uri.c new file mode 100644 index 000000000..128167cc5 --- /dev/null +++ b/src/cli/util/gnunet-uri.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/gnunet-uri.c | ||
23 | * @brief tool to dispatch URIs to the appropriate GNUnet helper process | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | |||
30 | /** | ||
31 | * Handler exit code | ||
32 | */ | ||
33 | static long unsigned int exit_code = 0; | ||
34 | |||
35 | /** | ||
36 | * Helper process we started. | ||
37 | */ | ||
38 | static struct GNUNET_OS_Process *p; | ||
39 | |||
40 | /** | ||
41 | * Pipe used to communicate shutdown via signal. | ||
42 | */ | ||
43 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
44 | |||
45 | |||
46 | /** | ||
47 | * Task triggered whenever we receive a SIGCHLD (child | ||
48 | * process died) or when user presses CTRL-C. | ||
49 | * | ||
50 | * @param cls closure, NULL | ||
51 | */ | ||
52 | static void | ||
53 | maint_child_death (void *cls) | ||
54 | { | ||
55 | enum GNUNET_OS_ProcessStatusType type; | ||
56 | |||
57 | (void) cls; | ||
58 | if ((GNUNET_OK != GNUNET_OS_process_status (p, &type, &exit_code)) || | ||
59 | (type != GNUNET_OS_PROCESS_EXITED)) | ||
60 | GNUNET_break (0 == GNUNET_OS_process_kill (p, GNUNET_TERM_SIG)); | ||
61 | GNUNET_OS_process_destroy (p); | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Main function that will be run by the scheduler. | ||
67 | * | ||
68 | * @param cls closure | ||
69 | * @param args remaining command-line arguments | ||
70 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
71 | * @param cfg configuration | ||
72 | */ | ||
73 | static void | ||
74 | run (void *cls, | ||
75 | char *const *args, | ||
76 | const char *cfgfile, | ||
77 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
78 | { | ||
79 | const char *uri; | ||
80 | const char *slash; | ||
81 | char *subsystem; | ||
82 | char *program; | ||
83 | struct GNUNET_SCHEDULER_Task *rt; | ||
84 | |||
85 | (void) cls; | ||
86 | (void) cfgfile; | ||
87 | if (NULL == (uri = args[0])) | ||
88 | { | ||
89 | fprintf (stderr, _ ("No URI specified on command line\n")); | ||
90 | return; | ||
91 | } | ||
92 | if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://"))) | ||
93 | { | ||
94 | fprintf (stderr, | ||
95 | _ ("Invalid URI: does not start with `%s'\n"), | ||
96 | "gnunet://"); | ||
97 | return; | ||
98 | } | ||
99 | uri += strlen ("gnunet://"); | ||
100 | if (NULL == (slash = strchr (uri, '/'))) | ||
101 | { | ||
102 | fprintf (stderr, _ ("Invalid URI: fails to specify subsystem\n")); | ||
103 | return; | ||
104 | } | ||
105 | subsystem = GNUNET_strndup (uri, slash - uri); | ||
106 | if (GNUNET_OK != | ||
107 | GNUNET_CONFIGURATION_get_value_string (cfg, "uri", subsystem, &program)) | ||
108 | { | ||
109 | fprintf (stderr, _ ("No handler known for subsystem `%s'\n"), subsystem); | ||
110 | GNUNET_free (subsystem); | ||
111 | return; | ||
112 | } | ||
113 | GNUNET_free (subsystem); | ||
114 | rt = GNUNET_SCHEDULER_add_read_file ( | ||
115 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
116 | GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), | ||
117 | &maint_child_death, | ||
118 | NULL); | ||
119 | p = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, | ||
120 | NULL, | ||
121 | NULL, | ||
122 | NULL, | ||
123 | program, | ||
124 | program, | ||
125 | args[0], | ||
126 | NULL); | ||
127 | GNUNET_free (program); | ||
128 | if (NULL == p) | ||
129 | GNUNET_SCHEDULER_cancel (rt); | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Signal handler called for SIGCHLD. Triggers the | ||
135 | * respective handler by writing to the trigger pipe. | ||
136 | */ | ||
137 | static void | ||
138 | sighandler_child_death () | ||
139 | { | ||
140 | static char c; | ||
141 | int old_errno = errno; /* back-up errno */ | ||
142 | |||
143 | GNUNET_break ( | ||
144 | 1 == | ||
145 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, | ||
146 | GNUNET_DISK_PIPE_END_WRITE), | ||
147 | &c, | ||
148 | sizeof(c))); | ||
149 | errno = old_errno; /* restore errno */ | ||
150 | } | ||
151 | |||
152 | |||
153 | /** | ||
154 | * The main function to handle gnunet://-URIs. | ||
155 | * | ||
156 | * @param argc number of arguments from the command line | ||
157 | * @param argv command line arguments | ||
158 | * @return 0 ok, 1 on error | ||
159 | */ | ||
160 | int | ||
161 | main (int argc, char *const *argv) | ||
162 | { | ||
163 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
164 | GNUNET_GETOPT_OPTION_END | ||
165 | }; | ||
166 | struct GNUNET_SIGNAL_Context *shc_chld; | ||
167 | int ret; | ||
168 | |||
169 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
170 | return 2; | ||
171 | sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); | ||
172 | GNUNET_assert (sigpipe != NULL); | ||
173 | shc_chld = | ||
174 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | ||
175 | ret = GNUNET_PROGRAM_run (argc, | ||
176 | argv, | ||
177 | "gnunet-uri URI", | ||
178 | gettext_noop ( | ||
179 | "Perform default-actions for GNUnet URIs"), | ||
180 | options, | ||
181 | &run, | ||
182 | NULL); | ||
183 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
184 | shc_chld = NULL; | ||
185 | GNUNET_DISK_pipe_close (sigpipe); | ||
186 | sigpipe = NULL; | ||
187 | GNUNET_free_nz ((void *) argv); | ||
188 | return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1; | ||
189 | } | ||
190 | |||
191 | |||
192 | /* end of gnunet-uri.c */ | ||
diff --git a/src/cli/util/meson.build b/src/cli/util/meson.build new file mode 100644 index 000000000..ed9c3105c --- /dev/null +++ b/src/cli/util/meson.build | |||
@@ -0,0 +1,62 @@ | |||
1 | executable ('gnunet-base32', | ||
2 | ['gnunet-base32.c'], | ||
3 | dependencies: [libgnunetutil_dep], | ||
4 | include_directories: [incdir, configuration_inc], | ||
5 | install: true, | ||
6 | install_dir: get_option('bindir')) | ||
7 | executable ('gnunet-config', | ||
8 | ['gnunet-config.c'], | ||
9 | dependencies: [libgnunetutil_dep], | ||
10 | include_directories: [incdir, configuration_inc], | ||
11 | install: true, | ||
12 | install_dir: get_option('bindir')) | ||
13 | executable ('gnunet-resolver', | ||
14 | ['gnunet-resolver.c'], | ||
15 | dependencies: [libgnunetutil_dep], | ||
16 | include_directories: [incdir, configuration_inc], | ||
17 | install: true, | ||
18 | install_dir: get_option('bindir')) | ||
19 | executable ('gnunet-ecc', | ||
20 | ['gnunet-ecc.c'], | ||
21 | dependencies: [libgnunetutil_dep, gcrypt_dep, m_dep], | ||
22 | include_directories: [incdir, configuration_inc], | ||
23 | install: true, | ||
24 | install_dir: get_option('bindir')) | ||
25 | executable ('gnunet-scrypt', | ||
26 | ['gnunet-scrypt.c'], | ||
27 | dependencies: [libgnunetutil_dep], | ||
28 | include_directories: [incdir, configuration_inc], | ||
29 | install: true, | ||
30 | install_dir: get_option('bindir')) | ||
31 | executable ('gnunet-uri', | ||
32 | ['gnunet-uri.c'], | ||
33 | dependencies: [libgnunetutil_dep], | ||
34 | include_directories: [incdir, configuration_inc], | ||
35 | install: true, | ||
36 | install_dir: get_option('bindir')) | ||
37 | if zbar_dep.found() | ||
38 | executable ('gnunet-qr', | ||
39 | ['gnunet-qr.c'], | ||
40 | dependencies: [zbar_dep, libgnunetutil_dep], | ||
41 | include_directories: [incdir, configuration_inc], | ||
42 | install: true, | ||
43 | install_dir: get_option('bindir')) | ||
44 | endif | ||
45 | executable ('gnunet-config-diff', | ||
46 | ['gnunet-config-diff.c'], | ||
47 | dependencies: [libgnunetutil_dep], | ||
48 | include_directories: [incdir, configuration_inc], | ||
49 | install: false) | ||
50 | |||
51 | executable ('gnunet-timeout', | ||
52 | ['gnunet-timeout.c'], | ||
53 | dependencies: [libgnunetutil_dep], | ||
54 | include_directories: [incdir, configuration_inc], | ||
55 | install: true, | ||
56 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
57 | executable ('gnunet-crypto-tvg', | ||
58 | ['gnunet-crypto-tvg.c'], | ||
59 | dependencies: [libgnunetutil_dep, json_dep], | ||
60 | include_directories: [incdir, configuration_inc], | ||
61 | install: false) | ||
62 | |||
diff --git a/src/cli/util/test_crypto_vectors.sh b/src/cli/util/test_crypto_vectors.sh new file mode 100755 index 000000000..40700a324 --- /dev/null +++ b/src/cli/util/test_crypto_vectors.sh | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/usr/bin/env bash | ||
2 | |||
3 | cat ./crypto-test-vectors.json | ./gnunet-crypto-tvg --verify | ||
diff --git a/src/cli/vpn/.gitignore b/src/cli/vpn/.gitignore new file mode 100644 index 000000000..dbd75eb25 --- /dev/null +++ b/src/cli/vpn/.gitignore | |||
@@ -0,0 +1 @@ | |||
gnunet-vpn | |||
diff --git a/src/cli/vpn/Makefile.am b/src/cli/vpn/Makefile.am new file mode 100644 index 000000000..0c7c459ef --- /dev/null +++ b/src/cli/vpn/Makefile.am | |||
@@ -0,0 +1,22 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | endif | ||
7 | |||
8 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
9 | |||
10 | libexecdir= $(pkglibdir)/libexec/ | ||
11 | |||
12 | plugindir = $(libdir)/gnunet | ||
13 | |||
14 | bin_PROGRAMS = \ | ||
15 | gnunet-vpn | ||
16 | |||
17 | gnunet_vpn_SOURCES = \ | ||
18 | gnunet-vpn.c | ||
19 | gnunet_vpn_LDADD = \ | ||
20 | $(top_builddir)/src/service/vpn/libgnunetvpn.la \ | ||
21 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
22 | $(GN_LIBINTL) | ||
diff --git a/src/cli/vpn/gnunet-vpn.c b/src/cli/vpn/gnunet-vpn.c new file mode 100644 index 000000000..a67e17016 --- /dev/null +++ b/src/cli/vpn/gnunet-vpn.c | |||
@@ -0,0 +1,364 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/vpn/gnunet-vpn.c | ||
23 | * @brief Tool to manually request VPN tunnels to be created | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_vpn_service.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Handle to vpn service. | ||
34 | */ | ||
35 | static struct GNUNET_VPN_Handle *handle; | ||
36 | |||
37 | /** | ||
38 | * Opaque redirection request handle. | ||
39 | */ | ||
40 | static struct GNUNET_VPN_RedirectionRequest *request; | ||
41 | |||
42 | /** | ||
43 | * Option -p: destination peer identity for service | ||
44 | */ | ||
45 | static char *peer_id; | ||
46 | |||
47 | /** | ||
48 | * Option -s: service name (hash to get service descriptor) | ||
49 | */ | ||
50 | static char *service_name; | ||
51 | |||
52 | /** | ||
53 | * Option -i: target IP | ||
54 | */ | ||
55 | static char *target_ip; | ||
56 | |||
57 | /** | ||
58 | * Option -4: IPv4 requested. | ||
59 | */ | ||
60 | static int ipv4; | ||
61 | |||
62 | /** | ||
63 | * Option -6: IPv6 requested. | ||
64 | */ | ||
65 | static int ipv6; | ||
66 | |||
67 | /** | ||
68 | * Option -t: TCP requested. | ||
69 | */ | ||
70 | static int tcp; | ||
71 | |||
72 | /** | ||
73 | * Option -u: UDP requested. | ||
74 | */ | ||
75 | static int udp; | ||
76 | |||
77 | /** | ||
78 | * Selected level of verbosity. | ||
79 | */ | ||
80 | static unsigned int verbosity; | ||
81 | |||
82 | /** | ||
83 | * Global return value. | ||
84 | */ | ||
85 | static int ret; | ||
86 | |||
87 | /** | ||
88 | * Option '-d': duration of the mapping | ||
89 | */ | ||
90 | static struct GNUNET_TIME_Relative duration = { 5 * 60 * 1000 }; | ||
91 | |||
92 | |||
93 | /** | ||
94 | * Shutdown. | ||
95 | */ | ||
96 | static void | ||
97 | do_disconnect (void *cls) | ||
98 | { | ||
99 | if (NULL != request) | ||
100 | { | ||
101 | GNUNET_VPN_cancel_request (request); | ||
102 | request = NULL; | ||
103 | } | ||
104 | if (NULL != handle) | ||
105 | { | ||
106 | GNUNET_VPN_disconnect (handle); | ||
107 | handle = NULL; | ||
108 | } | ||
109 | GNUNET_free (peer_id); | ||
110 | GNUNET_free (service_name); | ||
111 | GNUNET_free (target_ip); | ||
112 | } | ||
113 | |||
114 | |||
115 | /** | ||
116 | * Callback invoked from the VPN service once a redirection is | ||
117 | * available. Provides the IP address that can now be used to | ||
118 | * reach the requested destination. | ||
119 | * | ||
120 | * @param cls closure | ||
121 | * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error; | ||
122 | * will match 'result_af' from the request | ||
123 | * @param address IP address (struct in_addr or struct in_addr6, depending on 'af') | ||
124 | * that the VPN allocated for the redirection; | ||
125 | * traffic to this IP will now be redirected to the | ||
126 | * specified target peer; NULL on error | ||
127 | */ | ||
128 | static void | ||
129 | allocation_cb (void *cls, int af, const void *address) | ||
130 | { | ||
131 | char buf[INET6_ADDRSTRLEN]; | ||
132 | |||
133 | request = NULL; | ||
134 | switch (af) | ||
135 | { | ||
136 | case AF_INET6: | ||
137 | case AF_INET: | ||
138 | fprintf (stdout, "%s\n", inet_ntop (af, address, buf, sizeof(buf))); | ||
139 | break; | ||
140 | |||
141 | case AF_UNSPEC: | ||
142 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Error creating tunnel\n")); | ||
143 | ret = 1; | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | break; | ||
148 | } | ||
149 | GNUNET_SCHEDULER_shutdown (); | ||
150 | } | ||
151 | |||
152 | |||
153 | /** | ||
154 | * Main function that will be run by the scheduler. | ||
155 | * | ||
156 | * @param cls closure | ||
157 | * @param args remaining command-line arguments | ||
158 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
159 | * @param cfg configuration | ||
160 | */ | ||
161 | static void | ||
162 | run (void *cls, | ||
163 | char *const *args, | ||
164 | const char *cfgfile, | ||
165 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
166 | { | ||
167 | int dst_af; | ||
168 | int req_af; | ||
169 | struct GNUNET_PeerIdentity peer; | ||
170 | struct GNUNET_HashCode sd; | ||
171 | const void *addr; | ||
172 | struct in_addr v4; | ||
173 | struct in6_addr v6; | ||
174 | uint8_t protocol; | ||
175 | struct GNUNET_TIME_Absolute etime; | ||
176 | |||
177 | etime = GNUNET_TIME_relative_to_absolute (duration); | ||
178 | GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL); | ||
179 | handle = GNUNET_VPN_connect (cfg); | ||
180 | if (NULL == handle) | ||
181 | goto error; | ||
182 | req_af = AF_UNSPEC; | ||
183 | if (ipv4) | ||
184 | { | ||
185 | if (ipv6) | ||
186 | { | ||
187 | fprintf (stderr, | ||
188 | _ ("Option `%s' makes no sense with option `%s'.\n"), | ||
189 | "-4", | ||
190 | "-6"); | ||
191 | goto error; | ||
192 | } | ||
193 | req_af = AF_INET; | ||
194 | } | ||
195 | if (ipv6) | ||
196 | req_af = AF_INET6; | ||
197 | |||
198 | if (NULL == target_ip) | ||
199 | { | ||
200 | if (NULL == service_name) | ||
201 | { | ||
202 | fprintf (stderr, _ ("Option `%s' or `%s' is required.\n"), "-i", "-s"); | ||
203 | goto error; | ||
204 | } | ||
205 | if (NULL == peer_id) | ||
206 | { | ||
207 | fprintf (stderr, | ||
208 | _ ("Option `%s' is required when using option `%s'.\n"), | ||
209 | "-p", | ||
210 | "-s"); | ||
211 | goto error; | ||
212 | } | ||
213 | if (! (tcp | udp)) | ||
214 | { | ||
215 | fprintf (stderr, | ||
216 | _ ("Option `%s' or `%s' is required when using option `%s'.\n"), | ||
217 | "-t", | ||
218 | "-u", | ||
219 | "-s"); | ||
220 | goto error; | ||
221 | } | ||
222 | if (tcp & udp) | ||
223 | { | ||
224 | fprintf (stderr, | ||
225 | _ ("Option `%s' makes no sense with option `%s'.\n"), | ||
226 | "-t", | ||
227 | "-u"); | ||
228 | goto error; | ||
229 | } | ||
230 | if (tcp) | ||
231 | protocol = IPPROTO_TCP; | ||
232 | if (udp) | ||
233 | protocol = IPPROTO_UDP; | ||
234 | if (GNUNET_OK != | ||
235 | GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id, | ||
236 | strlen (peer_id), | ||
237 | &peer.public_key)) | ||
238 | { | ||
239 | fprintf (stderr, _ ("`%s' is not a valid peer identifier.\n"), peer_id); | ||
240 | goto error; | ||
241 | } | ||
242 | GNUNET_TUN_service_name_to_hash (service_name, &sd); | ||
243 | request = GNUNET_VPN_redirect_to_peer (handle, | ||
244 | req_af, | ||
245 | protocol, | ||
246 | &peer, | ||
247 | &sd, | ||
248 | etime, | ||
249 | &allocation_cb, | ||
250 | NULL); | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | if (1 != inet_pton (AF_INET6, target_ip, &v6)) | ||
255 | { | ||
256 | if (1 != inet_pton (AF_INET, target_ip, &v4)) | ||
257 | { | ||
258 | fprintf (stderr, _ ("`%s' is not a valid IP address.\n"), target_ip); | ||
259 | goto error; | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | dst_af = AF_INET; | ||
264 | addr = &v4; | ||
265 | } | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | dst_af = AF_INET6; | ||
270 | addr = &v6; | ||
271 | } | ||
272 | request = GNUNET_VPN_redirect_to_ip (handle, | ||
273 | req_af, | ||
274 | dst_af, | ||
275 | addr, | ||
276 | etime, | ||
277 | &allocation_cb, | ||
278 | NULL); | ||
279 | } | ||
280 | return; | ||
281 | |||
282 | error: | ||
283 | GNUNET_SCHEDULER_shutdown (); | ||
284 | ret = 1; | ||
285 | } | ||
286 | |||
287 | |||
288 | int | ||
289 | main (int argc, char *const *argv) | ||
290 | { | ||
291 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
292 | { GNUNET_GETOPT_option_flag ('4', | ||
293 | "ipv4", | ||
294 | gettext_noop ( | ||
295 | "request that result should be an IPv4 address"), | ||
296 | &ipv4), | ||
297 | |||
298 | GNUNET_GETOPT_option_flag ('6', | ||
299 | "ipv6", | ||
300 | gettext_noop ( | ||
301 | "request that result should be an IPv6 address"), | ||
302 | &ipv6), | ||
303 | |||
304 | GNUNET_GETOPT_option_relative_time ( | ||
305 | 'd', | ||
306 | "duration", | ||
307 | "TIME", | ||
308 | gettext_noop ("how long should the mapping be valid for new tunnels?"), | ||
309 | &duration), | ||
310 | |||
311 | GNUNET_GETOPT_option_string ('i', | ||
312 | "ip", | ||
313 | "IP", | ||
314 | gettext_noop ( | ||
315 | "destination IP for the tunnel"), | ||
316 | &target_ip), | ||
317 | |||
318 | GNUNET_GETOPT_option_string ( | ||
319 | 'p', | ||
320 | "peer", | ||
321 | "PEERID", | ||
322 | gettext_noop ("peer offering the service we would like to access"), | ||
323 | &peer_id), | ||
324 | |||
325 | GNUNET_GETOPT_option_string ('s', | ||
326 | "service", | ||
327 | "NAME", | ||
328 | gettext_noop ( | ||
329 | "name of the service we would like to access"), | ||
330 | &service_name), | ||
331 | |||
332 | GNUNET_GETOPT_option_flag ('t', | ||
333 | "tcp", | ||
334 | gettext_noop ("service is offered via TCP"), | ||
335 | &tcp), | ||
336 | |||
337 | GNUNET_GETOPT_option_flag ('u', | ||
338 | "udp", | ||
339 | gettext_noop ("service is offered via UDP"), | ||
340 | &udp), | ||
341 | |||
342 | GNUNET_GETOPT_option_verbose (&verbosity), | ||
343 | |||
344 | GNUNET_GETOPT_OPTION_END }; | ||
345 | |||
346 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
347 | return 2; | ||
348 | |||
349 | ret = | ||
350 | (GNUNET_OK == GNUNET_PROGRAM_run (argc, | ||
351 | argv, | ||
352 | "gnunet-vpn", | ||
353 | gettext_noop ("Setup tunnels via VPN."), | ||
354 | options, | ||
355 | &run, | ||
356 | NULL)) | ||
357 | ? ret | ||
358 | : 1; | ||
359 | GNUNET_free_nz ((void *) argv); | ||
360 | return ret; | ||
361 | } | ||
362 | |||
363 | |||
364 | /* end of gnunet-vpn.c */ | ||
diff --git a/src/cli/vpn/meson.build b/src/cli/vpn/meson.build new file mode 100644 index 000000000..477551f1d --- /dev/null +++ b/src/cli/vpn/meson.build | |||
@@ -0,0 +1,13 @@ | |||
1 | executable ('gnunet-vpn', | ||
2 | ['gnunet-vpn.c'], | ||
3 | dependencies: [libgnunetvpn_dep, | ||
4 | libgnunetutil_dep, | ||
5 | libgnunetstatistics_dep, | ||
6 | libgnunetregex_dep, | ||
7 | libgnunetcore_dep, | ||
8 | libgnunetcadet_dep, | ||
9 | libgnunetblock_dep], | ||
10 | include_directories: [incdir, configuration_inc, exitdir], | ||
11 | install: true, | ||
12 | install_dir: get_option('bindir')) | ||
13 | |||